Compare commits
22 Commits
plugin-int
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fe3a50bfd | ||
|
|
3743ca4d53 | ||
|
|
6feb84a1cc | ||
|
|
b090b27275 | ||
|
|
499631ba8e | ||
|
|
aa274e8c65 | ||
|
|
9e7daf7e37 | ||
|
|
e2581866bc | ||
|
|
037e475227 | ||
|
|
8c9cdb9603 | ||
|
|
a77702c885 | ||
|
|
1d882c6509 | ||
|
|
07060d7fea | ||
|
|
ef34b9b654 | ||
|
|
e22d6591e4 | ||
|
|
38493995ad | ||
|
|
74a0b39e3e | ||
|
|
a378d3a9d4 | ||
|
|
0f576fb748 | ||
|
|
119529c9a2 | ||
|
|
2c26119b10 | ||
|
|
cbccdbd05a |
39
.github/auto-assignees.yml
vendored
@@ -1,39 +0,0 @@
|
||||
---
|
||||
# This assigns a PR to its author
|
||||
addAssignees: author
|
||||
|
||||
reviewers:
|
||||
# The default reviewers
|
||||
defaults:
|
||||
- maintainers
|
||||
|
||||
groups:
|
||||
maintainers:
|
||||
- zubron
|
||||
- dsu-igeek
|
||||
- jenting
|
||||
- sseago
|
||||
- reasonerjt
|
||||
- ywk253100
|
||||
|
||||
tech-writer:
|
||||
- a-mccarthy
|
||||
|
||||
files:
|
||||
'site/**':
|
||||
- tech-writer
|
||||
'**/*.md':
|
||||
- tech-writer
|
||||
# Technical design requests are ".md" files but should
|
||||
# be reviewed by maintainers
|
||||
'/design/**':
|
||||
- maintainers
|
||||
|
||||
options:
|
||||
ignore_draft: true
|
||||
ignored_keywords:
|
||||
- WIP
|
||||
- wip
|
||||
- DO NOT MERGE
|
||||
enable_group_assignment: true
|
||||
number_of_reviewers: 2
|
||||
14
.github/auto_assign.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
addReviewers: true
|
||||
addAssignees: author
|
||||
|
||||
# Only require 2, random reviewers.
|
||||
# TODO expand this to support using reviewGroups
|
||||
numberOfReviewers: 2
|
||||
|
||||
reviewers:
|
||||
- nrb
|
||||
- ashish-amarnath
|
||||
- carlisia
|
||||
- zubron
|
||||
- dsu-igeek
|
||||
- jenting
|
||||
38
.github/stale.yml
vendored
@@ -1,38 +0,0 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 14
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- Epic
|
||||
- Area/CLI
|
||||
- Area/Cloud/AWS
|
||||
- Area/Cloud/Azure
|
||||
- Area/Cloud/GCP
|
||||
- Area/Cloud/vSphere
|
||||
- Area/CSI
|
||||
- Area/Design
|
||||
- Area/Documentation
|
||||
- Area/Plugins
|
||||
- Enhancement/User
|
||||
- kind/tech-debt
|
||||
- Needs investigation
|
||||
- P0 - Hair on fire
|
||||
- P1 - Important
|
||||
- P2 - Long-term important
|
||||
- P3 - Wouldn't it be nice if...
|
||||
- Product Requirements
|
||||
- Restic - GA
|
||||
- Restic
|
||||
- release-blocker
|
||||
- Security
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: staled
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
Closing the stale issue.
|
||||
15
.github/workflows/auto_assign_prs.yml
vendored
@@ -1,8 +1,6 @@
|
||||
---
|
||||
name: "Auto Assign Author"
|
||||
|
||||
# pull_request_target means that this will run on pull requests, but in
|
||||
# the context of the base repo. This should mean PRs from forks are supported.
|
||||
name: "Auto Assign PR Reviewers"
|
||||
# pull_request_target means that this will run on pull requests, but in the context of the base repo.
|
||||
# This should mean PRs from forks are supported.
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened, ready_for_review]
|
||||
@@ -12,8 +10,7 @@ jobs:
|
||||
add-reviews:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set the author of a PR as the assignee
|
||||
uses: kentaro-m/auto-assign-action@v1.1.1
|
||||
- uses: kentaro-m/auto-assign-action@v1.1.1
|
||||
with:
|
||||
configuration-path: ".github/auto-assignees.yml"
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
configuration-path: ".github/auto_assign.yml"
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
17
.github/workflows/auto_request_review.yml
vendored
@@ -1,17 +0,0 @@
|
||||
---
|
||||
name: "Auto Request Review"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, ready_for_review, reopened]
|
||||
|
||||
jobs:
|
||||
auto-request-review:
|
||||
name: Auto Request Review
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Request a PR review based on files types/paths, and/or groups the author belongs to
|
||||
uses: necojackarc/auto-request-review@v0.7.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
config: .github/auto-assignees.yml
|
||||
5
.github/workflows/crds-verify-kind.yaml
vendored
@@ -11,11 +11,6 @@ jobs:
|
||||
build-cli:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
id: go
|
||||
# Look for a CLI that's made for this PR
|
||||
- name: Fetch built CLI
|
||||
id: cache
|
||||
|
||||
10
.github/workflows/e2e-test-kind.yaml
vendored
@@ -11,11 +11,6 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
id: go
|
||||
# Look for a CLI that's made for this PR
|
||||
- name: Fetch built CLI
|
||||
id: cli-cache
|
||||
@@ -71,11 +66,6 @@ jobs:
|
||||
- 1.22.0
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
id: go
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install MinIO
|
||||
|
||||
7
.github/workflows/pr-ci-check.yml
vendored
@@ -5,13 +5,9 @@ jobs:
|
||||
name: Run CI
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
id: go
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Fetch cached go modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
@@ -19,5 +15,6 @@ jobs:
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Make ci
|
||||
run: make ci
|
||||
|
||||
2
.github/workflows/push.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: 1.15
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
|
||||
4
.gitignore
vendored
@@ -24,6 +24,8 @@ _testmain.go
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
debug
|
||||
|
||||
/velero
|
||||
.idea/
|
||||
|
||||
@@ -47,4 +49,4 @@ tilt-resources/tilt-settings.json
|
||||
tilt-resources/velero_v1_backupstoragelocation.yaml
|
||||
tilt-resources/deployment.yaml
|
||||
tilt-resources/restic.yaml
|
||||
tilt-resources/cloud
|
||||
tilt-resources/cloud
|
||||
@@ -13,7 +13,6 @@ If you're using Velero and want to add your organization to this list,
|
||||
<a href="https://banzaicloud.com/" border="0" target="_blank"><img alt="banzaicloud.com" src="site/static/img/adopters/banzaicloud.svg" height="50"></a>
|
||||
<a href="https://sighup.io/" border="0" target="_blank"><img alt="sighup.io" src="site/static/img/adopters/sighup.svg" height="50"></a>
|
||||
<a href="https://mayadata.io/" border="0" target="_blank"><img alt="mayadata.io" src="site/static/img/adopters/mayadata.svg" height="50"></a>
|
||||
<a href="https://www.replicated.com/" border="0" target="_blank"><img alt="replicated.com" src="site/static/img/adopters/replicated-logo-red.svg" height="50"></a>
|
||||
|
||||
## Success Stories
|
||||
|
||||
@@ -54,9 +53,6 @@ MayaData is a large user of Velero as well as a contributor. MayaData offers a D
|
||||
|
||||
**[Okteto][93]**
|
||||
Okteto integrates Velero in [Okteto Cloud][94] and [Okteto Enterprise][95] to periodically backup and restore our clusters for disaster recovery. Velero is also a core software building block to provide namespace cloning capabilities, a feature that allows our users cloning staging environments into their personal development namespace for providing production-like development environments.
|
||||
|
||||
**[Replicated][100]**<br>
|
||||
Replicated uses the Velero open source project to enable snapshots in [KOTS][101] to backup Kubernetes manifests & persistent volumes. In addition to the default functionality that Velero provides, [KOTS][101] provides a detailed interface in the [Admin Console][102] that can be used to manage the storage destination and schedule, and to perform and monitor the backup and restore process.
|
||||
|
||||
## Adding your organization to the list of Velero Adopters
|
||||
|
||||
@@ -106,7 +102,3 @@ If you would like to add your logo to a future `Adopters of Velero` section on [
|
||||
[93]: https://okteto.com
|
||||
[94]: https://cloud.okteto.com
|
||||
[95]: https://okteto.com/enterprise/
|
||||
|
||||
[100]: https://www.replicated.com
|
||||
[101]: https://kots.io
|
||||
[102]: https://kots.io/kotsadm/snapshots/overview/
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
## Current release:
|
||||
* [CHANGELOG-1.7.md][17]
|
||||
* [CHANGELOG-1.6.md][16]
|
||||
|
||||
## Older releases:
|
||||
* [CHANGELOG-1.6.md][16]
|
||||
* [CHANGELOG-1.5.md][15]
|
||||
* [CHANGELOG-1.4.md][14]
|
||||
* [CHANGELOG-1.3.md][13]
|
||||
@@ -20,7 +19,6 @@
|
||||
* [CHANGELOG-0.3.md][1]
|
||||
|
||||
|
||||
[17]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.7.md
|
||||
[16]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.6.md
|
||||
[15]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.5.md
|
||||
[14]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.4.md
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# 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.
|
||||
FROM --platform=$BUILDPLATFORM golang:1.16 as builder-env
|
||||
FROM --platform=$BUILDPLATFORM golang:1.15 as builder-env
|
||||
|
||||
ARG GOPROXY
|
||||
ARG PKG
|
||||
@@ -50,11 +50,13 @@ RUN mkdir -p /output/usr/bin && \
|
||||
go build -o /output/${BIN} \
|
||||
-ldflags "${LDFLAGS}" ${PKG}/cmd/${BIN}
|
||||
|
||||
FROM gcr.io/distroless/base-debian10:nonroot
|
||||
FROM ubuntu:focal
|
||||
|
||||
LABEL maintainer="Nolan Brubaker <brubakern@vmware.com>"
|
||||
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ca-certificates tzdata && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /output /
|
||||
|
||||
USER nonroot:nonroot
|
||||
USER nobody:nogroup
|
||||
|
||||
|
||||
@@ -6,29 +6,24 @@
|
||||
|
||||
| Maintainer | GitHub ID | Affiliation |
|
||||
| --------------- | --------- | ----------- |
|
||||
| Carlisia Thompson | [carlisia](https://github.com/carlisia) | [VMware](https://www.github.com/vmware/) |
|
||||
| Nolan Brubaker | [nrb](https://github.com/nrb) | [VMware](https://www.github.com/vmware/) |
|
||||
| Ashish Amarnath | [ashish-amarnath](https://github.com/ashish-amarnath) | [VMware](https://www.github.com/vmware/) |
|
||||
| Bridget McErlean | [zubron](https://github.com/zubron) | [VMware](https://www.github.com/vmware/) |
|
||||
| Dave Smith-Uchida | [dsu-igeek](https://github.com/dsu-igeek) | [VMware](https://www.github.com/vmware/) |
|
||||
| JenTing Hsiao | [jenting](https://github.com/jenting) | [SUSE](https://github.com/SUSE/)
|
||||
| Scott Seago | [sseago](https://github.com/sseago) | [OpenShift](https://github.com/openshift)
|
||||
| Daniel Jiang | [reasonerjt](https://github.com/reasonerjt) | [VMware](https://www.github.com/vmware/)
|
||||
| Wenkai Yin | [ywk253100](https://github.com/ywk253100) | [VMware](https://www.github.com/vmware/) |
|
||||
| JenTing Hsiao | [jenting](https://github.com/jenting) | [SUSE](https://github.com/SUSE/)
|
||||
|
||||
## Emeritus Maintainers
|
||||
* Adnan Abdulhussein ([prydonius](https://github.com/prydonius))
|
||||
* Andy Goldstein ([ncdc](https://github.com/ncdc))
|
||||
* Steve Kriss ([skriss](https://github.com/skriss))
|
||||
* Carlos Panato ([cpanato](https://github.com/cpanato))
|
||||
* Nolan Brubaker ([nrb](https://github.com/nrb))
|
||||
* Ashish Amarnath ([ashish-amarnath](https://github.com/ashish-amarnath))
|
||||
* Carlisia Thompson ([carlisia](https://github.com/carlisia))
|
||||
|
||||
## Velero Contributors & Stakeholders
|
||||
|
||||
| Feature Area | Lead |
|
||||
| ----------------------------- | :---------------------: |
|
||||
| Architect | Dave Smith-Uchida (dsu-igeek) |
|
||||
| Technical Lead | Daniel Jiang (reasonerjt) |
|
||||
| Kubernetes CSI Liaison | |
|
||||
| Deployment | JenTing Hsiao (jenting) |
|
||||
| Technical Lead | Nolan Brubaker (nrb) |
|
||||
| Kubernetes CSI Liaison | Nolan Brubaker (nrb), Ashish Amarnath (ashish-amarnath) |
|
||||
| Deployment | Carlisia Thompson (carlisia), Carlos Tadeu Panato Junior (cpanato), JenTing Hsiao (jenting) |
|
||||
| Community Management | Jonas Rosland (jonasrosland) |
|
||||
| Product Management | Eleanor Millman (eleanor-millman) |
|
||||
| Product Management | Michael Michael (michmike) |
|
||||
|
||||
18
Makefile
@@ -81,8 +81,8 @@ buildx not enabled, refusing to run this recipe
|
||||
see: https://velero.io/docs/main/build-from-source/#making-images-and-updating-velero for more info
|
||||
endef
|
||||
|
||||
# The version of restic binary to be downloaded
|
||||
RESTIC_VERSION ?= 0.12.1
|
||||
# The version of restic binary to be downloaded for power architecture
|
||||
RESTIC_VERSION ?= 0.12.0
|
||||
|
||||
CLI_PLATFORMS ?= linux-amd64 linux-arm linux-arm64 darwin-amd64 windows-amd64 linux-ppc64le
|
||||
BUILDX_PLATFORMS ?= $(subst -,/,$(ARCH))
|
||||
@@ -290,12 +290,12 @@ push-build-image:
|
||||
@# this target will push the build-image it assumes you already have docker
|
||||
@# credentials needed to accomplish this.
|
||||
@# Pushing will be skipped if a custom Dockerfile was used to build the image.
|
||||
ifneq "$(origin BUILDER_IMAGE_DOCKERFILE)" "file"
|
||||
@echo "Dockerfile for builder image has been overridden"
|
||||
@echo "Skipping push of custom image"
|
||||
else
|
||||
docker push $(BUILDER_IMAGE)
|
||||
endif
|
||||
ifneq "$(origin BUILDER_IMAGE_DOCKERFILE)" "file"
|
||||
@echo "Dockerfile for builder image has been overridden"
|
||||
@echo "Skipping push of custom image"
|
||||
else
|
||||
docker push $(BUILDER_IMAGE)
|
||||
endif
|
||||
|
||||
build-image-hugo:
|
||||
cd site && docker build --pull -t $(HUGO_IMAGE) .
|
||||
@@ -366,4 +366,4 @@ gen-docs:
|
||||
|
||||
.PHONY: test-e2e
|
||||
test-e2e: local
|
||||
$(MAKE) -e VERSION=$(VERSION) -C test/e2e run
|
||||
$(MAKE) -C test/e2e run
|
||||
|
||||
71
ROADMAP.md
@@ -15,28 +15,55 @@ We work with and rely on community feedback to focus our efforts to improve Vele
|
||||
The following table includes the current roadmap for Velero. If you have any questions or would like to contribute to Velero, please attend a [community meeting](https://velero.io/community/) to discuss with our team. If you don't know where to start, we are always looking for contributors that will help us reduce technical, automation, and documentation debt.
|
||||
Please take the timelines & dates as proposals and goals. Priorities and requirements change based on community feedback, roadblocks encountered, community contributions, etc. If you depend on a specific item, we encourage you to attend community meetings to get updated status information, or help us deliver that feature by contributing to Velero.
|
||||
|
||||
`Last Updated: October 2021`
|
||||
`Last Updated: March 2021`
|
||||
|
||||
#### 1.8.0 Roadmap (to be delivered January/February 2021)
|
||||
#### 1.7.0 Roadmap
|
||||
The release roadmap is split into Core items that are required for the release, desired items that may be removed from the
|
||||
release and opportunistic items that will be added to the release if possible.
|
||||
|
||||
|Issue|Description|Timeline|Notes|
|
||||
|---|---|---|---|
|
||||
|[4108](https://github.com/vmware-tanzu/velero/issues/4108), [4109](https://github.com/vmware-tanzu/velero/issues/4109)|Solution for CSI - Azure and AWS|2022 H1|Currently, Velero plugins for AWS and Azure cannot back up persistent volumes that were provisioned using the CSI driver. This will fix that.|
|
||||
|[3229](https://github.com/vmware-tanzu/velero/issues/3229),[4112](https://github.com/vmware-tanzu/velero/issues/4112)|Moving data mover functionality from the Velero Plugin for vSphere into Velero proper|2022 H1|This work is a precursor to decoupling the Astrolabe snapshotting infrastructure.|
|
||||
|[3533](https://github.com/vmware-tanzu/velero/issues/3533)|Upload Progress Monitoring|2022 H1|Finishing up the work done in the 1.7 timeframe. The data mover work depends on this.|
|
||||
|[1975](https://github.com/vmware-tanzu/velero/issues/1975)|Test dual stack mode|2022 H1|We already tested IPv6, but we want to confirm that dual stack mode works as well.|
|
||||
|[2082](https://github.com/vmware-tanzu/velero/issues/2082)|Delete Backup CRs on removing target location. |2022 H1||
|
||||
|[3516](https://github.com/vmware-tanzu/velero/issues/3516)|Restore issue with MutatingWebhookConfiguration v1beta1 API version|2022 H1||
|
||||
|[2308](https://github.com/vmware-tanzu/velero/issues/2308)|Restoring nodePort service that has nodePort preservation always fails if service already exists in the namespace|2022 H1||
|
||||
|[4115](https://github.com/vmware-tanzu/velero/issues/4115)|Support for multiple set of credentials for VolumeSnapshotLocations|2022 H1||
|
||||
|[1980](https://github.com/vmware-tanzu/velero/issues/1980)|Velero triggers backup immediately for scheduled backups|2022 H1||
|
||||
|[4067](https://github.com/vmware-tanzu/velero/issues/4067)|Pre and post backup and restore hooks|2022 H1||
|
||||
|[3742](https://github.com/vmware-tanzu/velero/issues/3742)|Carvel packaging for Velero for vSphere|2022 H1|AWS and Azure have been completed already.|
|
||||
|[3285](https://github.com/vmware-tanzu/velero/issues/3285)|Design doc for Velero plugin versioning|2022 H1||
|
||||
|[4231](https://github.com/vmware-tanzu/velero/issues/4231)|Technical health (prioritizing giving developers confidence and saving developers time)|2022 H1|More automated tests (especially the pre-release manual tests) and more automation of the running of tests.|
|
||||
|[4110](https://github.com/vmware-tanzu/velero/issues/4110)|Solution for CSI - GCP|2022 H1|Currently, the Velero plugin for GCP cannot back up persistent volumes that were provisioned using the CSI driver. This will fix that.|
|
||||
|[3742](https://github.com/vmware-tanzu/velero/issues/3742)|Carvel packaging for Velero for restic|2022 H1|AWS and Azure have been completed already.|
|
||||
|[3454](https://github.com/vmware-tanzu/velero/issues/3454),[4134](https://github.com/vmware-tanzu/velero/issues/4134),[4135](https://github.com/vmware-tanzu/velero/issues/4135)|Kubebuilder tech debt|2022 H1||
|
||||
|[4111](https://github.com/vmware-tanzu/velero/issues/4111)|Ignore items returned by ItemSnapshotter.AlsoHandles during backup|2022 H1|This will enable backup of complex objects, because we can then tell Velero to ignore things that were already backed up when Velero was previously called recursively.|
|
||||
##### Core items
|
||||
|
||||
Other work may make it into the 1.8 release, but this is the work that will be prioritized first.
|
||||
|Issue|Description|
|
||||
|---|---|
|
||||
|[3493](https://github.com/vmware-tanzu/velero/issues/3493)|[Carvel](https://github.com/vmware-tanzu/velero/issues/3493) based installation (in addition to the existing *velero install* CLI).|
|
||||
|[3531](https://github.com/vmware-tanzu/velero/issues/3531)|Test plan for Velero|
|
||||
|[675](https://github.com/vmware-tanzu/velero/issues/675)|Velero command to generate debugging information. Will integrate with [Crashd - Crash Diagnostics](https://github.com/vmware-tanzu/velero/issues/675)|
|
||||
|[2066](https://github.com/vmware-tanzu/velero/issues/2066)|CSI Snapshots GA|
|
||||
|[3285](https://github.com/vmware-tanzu/velero/issues/3285)|Support Velero plugin versioning|
|
||||
|[1975](https://github.com/vmware-tanzu/velero/issues/1975)|IPV6 support|
|
||||
|
||||
|
||||
|
||||
##### Desired items
|
||||
|Issue|Description|
|
||||
|---|---|
|
||||
|[3533](https://github.com/vmware-tanzu/velero/issues/3533)|Upload Progress Monitoring|
|
||||
|[2922](https://github.com/vmware-tanzu/velero/issues/2922)|Plugin timeouts|
|
||||
|[3500](https://github.com/vmware-tanzu/velero/issues/3500)|Use distroless containers as a base|
|
||||
|[3535](https://github.com/vmware-tanzu/velero/issues/3535)|Design doc for multiple cluster support|
|
||||
|[3536](https://github.com/vmware-tanzu/velero/issues/3536)|Manifest for backup/restore|
|
||||
|
||||
##### Opportunistic items
|
||||
|Issue|Description|
|
||||
|---|---|
|
||||
|Issues TBD|Controller migrations|
|
||||
|
||||
#### Long term roadmap items
|
||||
|Theme|Description|Timeline|
|
||||
|---|---|---|
|
||||
|Restic Improvements|Introduce improvements in annotating resources for Restic backup|TBD|
|
||||
|Extensibility|Add restore hooks for enhanced recovery scenarios|TBD|
|
||||
|CSI|Continue improving the CSI snapshot capabilities and participate in the upstream K8s CSI community|1.7.0 + Long running (dependent on CSI working group)|
|
||||
|Backup/Restore|Improvements to long-running copy operations from a performance and reliability standpoint|1.7.0|
|
||||
|Quality/Reliability| Enable automated end-to-end testing |1.6.0|
|
||||
|UX|Improvements to install and configuration user experience|Dec 2020|
|
||||
|Restic Improvements|Improve the use of Restic in Velero and offer stable support|TBD|
|
||||
|Perf & Scale|Introduce a scalable model by using a worker pod for each backup/restore operation and improve operations|1.8.0|
|
||||
|Backup/Restore|Better backup and restore semantics for certain Kubernetes resources like stateful sets, operators|2.0|
|
||||
|Security|Enable the use of custom credential providers|1.6.0|
|
||||
|Self-Service & Multitenancy|Reduce friction by enabling developers to backup their namespaces via self-service. Introduce a Velero multi-tenancy model, enabling owners of namespaces to backup and restore within their access scope|TBD|
|
||||
|Backup/Restore|Cross availability zone or region backup and restore|TBD|
|
||||
|Application Consistency|Offer blueprints for backing up and restoring popular applications|TBD|
|
||||
|Backup/Restore|Data only backup and restore|TBD|
|
||||
|Backup/Restore|Introduce the ability to overwrite existing objects during a restore|TBD|
|
||||
|Backup/Restore|What-if dry run for backup and restore|1.7.0|
|
||||
|
||||
8
Tiltfile
@@ -16,7 +16,7 @@ k8s_yaml([
|
||||
|
||||
# default values
|
||||
settings = {
|
||||
"default_registry": "docker.io/velero",
|
||||
"default_registry": "",
|
||||
"enable_restic": False,
|
||||
"enable_debug": False,
|
||||
"debug_continue_on_start": True, # Continue the velero process by default when in debug mode
|
||||
@@ -50,7 +50,7 @@ git_sha = str(local("git rev-parse HEAD", quiet = True, echo_off = True)).strip(
|
||||
|
||||
tilt_helper_dockerfile_header = """
|
||||
# Tilt image
|
||||
FROM golang:1.16.6 as tilt-helper
|
||||
FROM golang:1.15.3 as tilt-helper
|
||||
|
||||
# Support live reloading with Tilt
|
||||
RUN wget --output-document /restart.sh --quiet https://raw.githubusercontent.com/windmilleng/rerun-process-wrapper/master/restart.sh && \
|
||||
@@ -90,14 +90,14 @@ def get_debug_flag():
|
||||
# Set up a local_resource build of the Velero binary. The binary is written to _tiltbuild/velero.
|
||||
local_resource(
|
||||
"velero_server_binary",
|
||||
cmd = 'cd ' + '.' + ';mkdir -p _tiltbuild;PKG=. BIN=velero GOOS=linux GOARCH=amd64 GIT_SHA=' + git_sha + ' VERSION=main GIT_TREE_STATE=dirty OUTPUT_DIR=_tiltbuild ' + get_debug_flag() + ' REGISTRY=' + settings.get("default_registry") + ' ./hack/build.sh',
|
||||
cmd = 'cd ' + '.' + ';mkdir -p _tiltbuild;PKG=. BIN=velero GOOS=linux GOARCH=amd64 GIT_SHA=' + git_sha + ' VERSION=main GIT_TREE_STATE=dirty OUTPUT_DIR=_tiltbuild ' + get_debug_flag() + ' ./hack/build.sh',
|
||||
deps = ["cmd", "internal", "pkg"],
|
||||
ignore = ["pkg/cmd"],
|
||||
)
|
||||
|
||||
local_resource(
|
||||
"velero_local_binary",
|
||||
cmd = 'cd ' + '.' + ';mkdir -p _tiltbuild/local;PKG=. BIN=velero GOOS=' + local_goos + ' GOARCH=amd64 GIT_SHA=' + git_sha + ' VERSION=main GIT_TREE_STATE=dirty OUTPUT_DIR=_tiltbuild/local ' + get_debug_flag() + ' REGISTRY=' + settings.get("default_registry") + ' ./hack/build.sh',
|
||||
cmd = 'cd ' + '.' + ';mkdir -p _tiltbuild/local;PKG=. BIN=velero GOOS=' + local_goos + ' GOARCH=amd64 GIT_SHA=' + git_sha + ' VERSION=main GIT_TREE_STATE=dirty OUTPUT_DIR=_tiltbuild/local ' + get_debug_flag() + ' ./hack/build.sh',
|
||||
deps = ["internal", "pkg/cmd"],
|
||||
)
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# Velero Assets
|
||||
|
||||
This folder contains logo images for Velero in gray (for light backgrounds) and white (for dark backgrounds like black tshirts or dark mode!) – horizontal and stacked… in .eps and .svg.
|
||||
|
||||
## Some general guidelines for usage
|
||||
|
||||
• Don’t alter the logos/graphics: resize, reformat, recolor. Keep them intact.
|
||||
|
||||
• Don’t separate the word mark (Velero) from the icon) – we are still building a strong name and identity – and the logo by itself doesn’t have any strong recognition or association with as yet: so best practice keep the two together. Nike kept its name with the swoosh for quite some time before the swoosh became iconic.
|
||||
|
||||
• Don’t append the name to another brand – let it stand alone!
|
||||
|
Before Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 36 KiB |
@@ -1,105 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 431.3 150" style="enable-background:new 0 0 431.3 150;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:none;}
|
||||
.st2{fill:#009BDB;}
|
||||
.st3{fill:#717074;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st3" d="M196.6,55.8l-18.2,41.2h-5.1l-18.2-41.2h5.1l15.7,35.5l15.6-35.5H196.6z"/>
|
||||
<path class="st3" d="M206.6,60.3v13.6h22.4v4.4h-22.4v14.3h24.8v4.4h-29.6V55.8h29.6v4.4H206.6z"/>
|
||||
<path class="st3" d="M265.7,92.6v4.4h-27.2V55.8h4.7v36.8H265.7z"/>
|
||||
<path class="st3" d="M275.7,60.3v13.6h22.4v4.4h-22.4v14.3h24.8v4.4H271V55.8h29.6v4.4H275.7z"/>
|
||||
<path class="st3" d="M338,75.3c-1.1,1.9-2.6,3.4-4.6,4.6c-2,1.2-4.2,1.9-6.6,2.2l10.5,14.9H332l-10.4-14.8h-9.1v14.8h-4.7V55.8
|
||||
h16.7c2.8,0,5.4,0.6,7.7,1.7c2.3,1.1,4.1,2.7,5.5,4.7c1.3,2,2,4.3,2,6.8C339.6,71.3,339.1,73.4,338,75.3z M312.4,77.8h11.2
|
||||
c3.4,0,6.1-0.8,8.2-2.3c2.1-1.6,3.1-3.7,3.1-6.4c0-2.7-1-4.9-3.1-6.4c-2.1-1.6-4.8-2.3-8.2-2.3h-11.2V77.8z"/>
|
||||
<path class="st3" d="M354.4,94.9c-3.3-1.9-5.8-4.5-7.8-7.8c-1.9-3.3-2.9-6.8-2.9-10.6c0-3.8,1-7.3,2.9-10.6
|
||||
c1.9-3.3,4.5-5.9,7.8-7.8c3.3-1.9,6.8-2.9,10.5-2.9c3.8,0,7.2,1,10.5,2.9c3.2,1.9,5.8,4.5,7.7,7.8c1.9,3.3,2.9,6.8,2.9,10.6
|
||||
c0,3.8-1,7.3-2.9,10.6c-1.9,3.3-4.5,5.9-7.7,7.8c-3.2,1.9-6.7,2.9-10.5,2.9C361.2,97.8,357.7,96.8,354.4,94.9z M373,91.1
|
||||
c2.5-1.6,4.5-3.6,6-6.2c1.5-2.6,2.2-5.4,2.2-8.5c0-3-0.7-5.8-2.2-8.4c-1.5-2.6-3.5-4.7-6-6.2c-2.5-1.5-5.2-2.3-8.1-2.3
|
||||
c-2.9,0-5.6,0.8-8.1,2.3c-2.5,1.5-4.5,3.6-6,6.2c-1.5,2.6-2.2,5.4-2.2,8.4c0,3,0.8,5.9,2.2,8.5c1.5,2.6,3.5,4.7,6,6.2
|
||||
s5.2,2.3,8.1,2.3C367.8,93.5,370.5,92.7,373,91.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M132.2,77.1c0-0.7,0.1-1.4,0.1-2s0-1.4-0.1-2V77.1z"/>
|
||||
<path class="st2" d="M117,109.3c-3.3,0-4.9,1.2-6.7,2.7c-2,1.6-4.2,3.4-8.6,3.4c-4.3,0-6.6-1.8-8.6-3.4c-1.8-1.4-3.4-2.7-6.7-2.7
|
||||
c-3.3,0-4.9,1.2-6.7,2.7c-2,1.6-4.2,3.4-8.6,3.4c-4.3,0-6.6-1.8-8.6-3.4c-1.8-1.4-3.4-2.7-6.7-2.7c-3.3,0-4.9,1.2-6.7,2.7
|
||||
c-1.3,1-2.7,2.2-4.8,2.8c8.3,7.3,18.9,12,30.5,13c0.3,0,0.6,0.1,1,0.1c1.1,0.1,2.3,0.1,3.4,0.1c1.2,0,2.3-0.1,3.4-0.1
|
||||
c0.3,0,0.6,0,1-0.1c14.2-1.2,26.8-8,35.6-18.2C118.7,109.4,117.9,109.3,117,109.3z"/>
|
||||
<path class="st2" d="M40.8,69.8c3.6,0,5.3-1.4,7.2-2.9c2-1.6,4-3.2,8-3.2s6.1,1.6,8,3.2c1.9,1.5,3.6,2.9,7.2,2.9
|
||||
c3.6,0,5.3-1.4,7.2-2.9c2-1.6,4-3.2,8-3.2c4,0,6.1,1.6,8,3.2c1.9,1.5,3.6,2.9,7.2,2.9c3.6,0,5.3-1.4,7.2-2.9c2-1.6,4-3.2,8-3.2
|
||||
c4,0,6.1,1.6,8,3.2c1.8,1.5,3.6,2.8,7,2.9c-0.5-4.8-1.6-9.5-3.3-13.8c-1.8-0.6-3.1-1.6-4.4-2.6c-1.9-1.5-3.7-2.9-7.4-2.9
|
||||
c-3.7,0-5.5,1.4-7.4,2.9c-1.9,1.5-3.9,3.1-7.9,3.1c-4,0-5.9-1.6-7.9-3.1c-1.9-1.5-3.7-2.9-7.4-2.9c-3.7,0-5.5,1.4-7.4,2.9
|
||||
c-1.9,1.5-3.9,3.1-7.9,3.1c-4,0-5.9-1.6-7.9-3.1c-1.9-1.5-3.7-2.9-7.4-2.9c-3.7,0-5.5,1.4-7.4,2.9c-1.9,1.5-3.9,3.1-7.9,3.1
|
||||
c-4,0-5.9-1.6-7.9-3.1c-0.4-0.3-0.9-0.7-1.3-1c-1.7,3.6-3.1,7.5-3.9,11.6c2.7,0.5,4.3,1.7,5.9,3C35.5,68.4,37.2,69.8,40.8,69.8z"
|
||||
/>
|
||||
<path class="st2" d="M40.8,55.7c3.7,0,5.5-1.4,7.4-2.9c1.9-1.5,3.9-3.1,7.9-3.1c4,0,5.9,1.6,7.9,3.1c1.9,1.5,3.7,2.9,7.4,2.9
|
||||
c3.7,0,5.5-1.4,7.4-2.9c1.9-1.5,3.9-3.1,7.9-3.1c4,0,5.9,1.6,7.9,3.1c1.9,1.5,3.7,2.9,7.4,2.9c3.7,0,5.5-1.4,7.4-2.9
|
||||
c1.9-1.5,3.9-3.1,7.9-3.1c4,0,5.9,1.6,7.9,3.1c1.1,0.8,2.1,1.7,3.4,2.2c-7.9-19.2-26.9-32.8-48.9-32.8c-20.8,0-38.7,12-47.4,29.5
|
||||
c0.5,0.4,1,0.7,1.4,1.1C35.3,54.2,37.1,55.7,40.8,55.7z"/>
|
||||
<path class="st2" d="M117,94.6c-3.4,0-5,1.3-6.8,2.8c-1.9,1.5-4.1,3.3-8.4,3.3c-4.2,0-6.4-1.7-8.4-3.3c-1.9-1.5-3.5-2.8-6.8-2.8
|
||||
c-3.4,0-5,1.3-6.8,2.8c-1.9,1.5-4.1,3.3-8.4,3.3c-4.2,0-6.4-1.7-8.4-3.3c-1.9-1.5-3.5-2.8-6.8-2.8c-3.4,0-5,1.3-6.8,2.8
|
||||
c-1.9,1.5-4.1,3.3-8.4,3.3c-4.2,0-6.4-1.7-8.4-3.3c-0.5-0.4-1-0.8-1.5-1.1c2.6,6,6.3,11.4,10.9,16c2.6-0.2,4-1.4,5.6-2.6
|
||||
c2-1.6,4.2-3.4,8.6-3.4s6.6,1.8,8.6,3.4c1.8,1.4,3.4,2.7,6.7,2.7c3.3,0,4.9-1.2,6.7-2.7c2-1.6,4.2-3.4,8.6-3.4
|
||||
c4.3,0,6.6,1.8,8.6,3.4c1.8,1.4,3.4,2.7,6.7,2.7c3.3,0,4.9-1.2,6.7-2.7c2-1.6,4.2-3.4,8.6-3.4c1.8,0,3.3,0.3,4.5,0.8
|
||||
c1.9-2.5,3.5-5.1,5-7.9c-1-0.6-1.8-1.2-2.6-1.8C122,95.8,120.3,94.6,117,94.6z"/>
|
||||
<path class="st2" d="M132.1,71.2c-4,0-6-1.6-8-3.2c-1.9-1.5-3.6-2.9-7.2-2.9c-3.6,0-5.3,1.4-7.2,2.9c-2,1.6-4,3.2-8,3.2
|
||||
c-4,0-6.1-1.6-8-3.2c-1.9-1.5-3.6-2.9-7.2-2.9c-3.6,0-5.3,1.4-7.2,2.9c-2,1.6-4,3.2-8,3.2c-4,0-6.1-1.6-8-3.2
|
||||
c-1.9-1.5-3.6-2.9-7.2-2.9c-3.6,0-5.3,1.4-7.2,2.9c-2,1.6-4,3.2-8,3.2c-4,0-6.1-1.6-8-3.2c-1.5-1.2-3-2.3-5.3-2.7
|
||||
c-0.5,2.5-0.8,5.1-0.9,7.7c0,0.7-0.1,1.4-0.1,2c0,0.7,0,1.4,0.1,2c0,0.3,0,0.6,0,0.9c3.5,0.3,5.4,1.8,7.2,3.2
|
||||
c1.8,1.4,3.5,2.8,7,2.8c3.5,0,5.2-1.4,7-2.8c1.9-1.5,4.1-3.2,8.2-3.2s6.3,1.7,8.2,3.2c1.8,1.4,3.5,2.8,7,2.8c3.5,0,5.2-1.4,7-2.8
|
||||
c1.9-1.5,4.1-3.2,8.2-3.2c4.1,0,6.3,1.7,8.2,3.2c1.8,1.4,3.5,2.8,7,2.8c3.5,0,5.2-1.4,7-2.8c1.9-1.5,4.1-3.2,8.2-3.2
|
||||
c4.1,0,6.3,1.7,8.2,3.2c1.7,1.3,3.3,2.6,6.3,2.8c0.3-1.6,0.5-3.2,0.6-4.9c0-0.6,0.1-1.3,0.1-1.9V73
|
||||
C132.2,72.4,132.1,71.2,132.1,71.2z"/>
|
||||
<path class="st2" d="M117,79.9c-3.5,0-5.2,1.4-7,2.8c-1.9,1.5-4.1,3.2-8.2,3.2c-4.1,0-6.3-1.7-8.2-3.2c-1.8-1.4-3.5-2.8-7-2.8
|
||||
c-3.5,0-5.2,1.4-7,2.8c-1.9,1.5-4.1,3.2-8.2,3.2c-4.1,0-6.3-1.7-8.2-3.2c-1.8-1.4-3.5-2.8-7-2.8c-3.5,0-5.2,1.4-7,2.8
|
||||
c-1.9,1.5-4.1,3.2-8.2,3.2c-4.1,0-6.3-1.7-8.2-3.2c-1.6-1.3-3.1-2.5-5.8-2.8c0.4,4.5,1.4,8.7,2.8,12.8c1.9,0.6,3.2,1.7,4.4,2.7
|
||||
c1.9,1.5,3.5,2.8,6.8,2.8c3.4,0,5-1.3,6.8-2.8c1.9-1.5,4.1-3.3,8.4-3.3c4.2,0,6.4,1.7,8.4,3.3c1.9,1.5,3.5,2.8,6.8,2.8
|
||||
c3.4,0,5-1.3,6.8-2.8c1.9-1.5,4.1-3.3,8.4-3.3c4.2,0,6.4,1.7,8.4,3.3c1.9,1.5,3.5,2.8,6.8,2.8c3.4,0,5-1.3,6.8-2.8
|
||||
c1.9-1.5,4.1-3.3,8.4-3.3c4.2,0,6.4,1.7,8.4,3.3c0.7,0.6,1.4,1.1,2.2,1.6c1.6-3.5,2.8-7.2,3.6-11.1c-3.5-0.3-5.4-1.8-7.2-3.2
|
||||
C122.2,81.2,120.4,79.9,117,79.9z"/>
|
||||
<path class="st0" d="M108.4,109.6c-1.8,1.4-3.4,2.7-6.7,2.7c-3.3,0-4.9-1.2-6.7-2.7c-2-1.6-4.2-3.4-8.6-3.4
|
||||
c-4.3,0-6.6,1.8-8.6,3.4c-1.8,1.4-3.4,2.7-6.7,2.7c-3.3,0-4.9-1.2-6.7-2.7c-2-1.6-4.2-3.4-8.6-3.4s-6.6,1.8-8.6,3.4
|
||||
c-1.6,1.3-3,2.4-5.6,2.6c0.9,0.9,1.8,1.7,2.7,2.5c2.1-0.6,3.5-1.8,4.8-2.8c1.8-1.4,3.4-2.7,6.7-2.7c3.3,0,4.9,1.2,6.7,2.7
|
||||
c2,1.6,4.2,3.4,8.6,3.4c4.3,0,6.6-1.8,8.6-3.4c1.8-1.4,3.4-2.7,6.7-2.7c3.3,0,4.9,1.2,6.7,2.7c2,1.6,4.2,3.4,8.6,3.4
|
||||
c4.3,0,6.6-1.8,8.6-3.4c1.8-1.4,3.4-2.7,6.7-2.7c0.9,0,1.7,0.1,2.4,0.3c0.7-0.8,1.4-1.7,2-2.5c-1.2-0.5-2.7-0.8-4.5-0.8
|
||||
C112.6,106.2,110.4,108,108.4,109.6z"/>
|
||||
<path class="st0" d="M117,92.1c-4.2,0-6.4,1.7-8.4,3.3c-1.9,1.5-3.5,2.8-6.8,2.8c-3.4,0-5-1.3-6.8-2.8c-1.9-1.5-4.1-3.3-8.4-3.3
|
||||
c-4.2,0-6.4,1.7-8.4,3.3c-1.9,1.5-3.5,2.8-6.8,2.8c-3.4,0-5-1.3-6.8-2.8c-1.9-1.5-4.1-3.3-8.4-3.3c-4.2,0-6.4,1.7-8.4,3.3
|
||||
c-1.9,1.5-3.5,2.8-6.8,2.8c-3.4,0-5-1.3-6.8-2.8c-1.2-1-2.5-2-4.4-2.7c0.4,1.2,0.9,2.3,1.4,3.5c0.5,0.3,1,0.7,1.5,1.1
|
||||
c1.9,1.5,4.1,3.3,8.4,3.3c4.2,0,6.4-1.7,8.4-3.3c1.9-1.5,3.5-2.8,6.8-2.8c3.4,0,5,1.3,6.8,2.8c1.9,1.5,4.1,3.3,8.4,3.3
|
||||
c4.2,0,6.4-1.7,8.4-3.3c1.9-1.5,3.5-2.8,6.8-2.8c3.4,0,5,1.3,6.8,2.8c1.9,1.5,4.1,3.3,8.4,3.3c4.2,0,6.4-1.7,8.4-3.3
|
||||
c1.9-1.5,3.5-2.8,6.8-2.8c3.4,0,5,1.3,6.8,2.8c0.8,0.6,1.6,1.3,2.6,1.8c0.4-0.7,0.7-1.5,1.1-2.2c-0.8-0.4-1.4-1-2.2-1.6
|
||||
C123.4,93.8,121.2,92.1,117,92.1z"/>
|
||||
<path class="st0" d="M117,77.9c-4.1,0-6.3,1.7-8.2,3.2c-1.8,1.4-3.5,2.8-7,2.8c-3.5,0-5.2-1.4-7-2.8c-1.9-1.5-4.1-3.2-8.2-3.2
|
||||
c-4.1,0-6.3,1.7-8.2,3.2c-1.8,1.4-3.5,2.8-7,2.8c-3.5,0-5.2-1.4-7-2.8c-1.9-1.5-4.1-3.2-8.2-3.2c-4.1,0-6.3,1.7-8.2,3.2
|
||||
c-1.8,1.4-3.5,2.8-7,2.8c-3.5,0-5.2-1.4-7-2.8c-1.7-1.4-3.7-2.9-7.2-3.2c0,0.6,0.1,1.3,0.1,1.9c2.7,0.3,4.2,1.5,5.8,2.8
|
||||
c1.9,1.5,4.1,3.2,8.2,3.2c4.1,0,6.3-1.7,8.2-3.2c1.8-1.4,3.5-2.8,7-2.8c3.5,0,5.2,1.4,7,2.8c1.9,1.5,4.1,3.2,8.2,3.2
|
||||
c4.1,0,6.3-1.7,8.2-3.2c1.8-1.4,3.5-2.8,7-2.8c3.5,0,5.2,1.4,7,2.8c1.9,1.5,4.1,3.2,8.2,3.2c4.1,0,6.3-1.7,8.2-3.2
|
||||
c1.8-1.4,3.5-2.8,7-2.8c3.5,0,5.2,1.4,7,2.8c1.7,1.4,3.7,2.9,7.2,3.2c0.1-0.6,0.2-1.3,0.3-1.9c-3-0.2-4.6-1.4-6.3-2.8
|
||||
C123.3,79.6,121.1,77.9,117,77.9z"/>
|
||||
<path class="st0" d="M40.8,71.2c4,0,6.1-1.6,8-3.2c1.9-1.5,3.6-2.9,7.2-2.9c3.6,0,5.3,1.4,7.2,2.9c2,1.6,4,3.2,8,3.2
|
||||
c4,0,6.1-1.6,8-3.2c1.9-1.5,3.6-2.9,7.2-2.9c3.6,0,5.3,1.4,7.2,2.9c2,1.6,4,3.2,8,3.2c4,0,6.1-1.6,8-3.2c1.9-1.5,3.6-2.9,7.2-2.9
|
||||
c3.6,0,5.3,1.4,7.2,2.9c1.9,1.5,4,3.1,8,3.2l0-0.1c0-0.4-0.1-0.8-0.1-1.3c-3.4-0.1-5.2-1.4-7-2.9c-2-1.6-4-3.2-8-3.2
|
||||
c-4,0-6.1,1.6-8,3.2c-1.9,1.5-3.6,2.9-7.2,2.9c-3.6,0-5.3-1.4-7.2-2.9c-2-1.6-4-3.2-8-3.2c-4,0-6.1,1.6-8,3.2
|
||||
c-1.9,1.5-3.6,2.9-7.2,2.9c-3.6,0-5.3-1.4-7.2-2.9c-2-1.6-4-3.2-8-3.2s-6.1,1.6-8,3.2c-1.9,1.5-3.6,2.9-7.2,2.9
|
||||
c-3.6,0-5.3-1.4-7.2-2.9c-1.6-1.3-3.2-2.5-5.9-3c-0.1,0.4-0.2,0.9-0.3,1.3c2.4,0.4,3.8,1.5,5.3,2.7
|
||||
C34.7,69.6,36.7,71.2,40.8,71.2z"/>
|
||||
<path class="st0" d="M40.8,56.5c4,0,5.9-1.6,7.9-3.1c1.9-1.5,3.7-2.9,7.4-2.9c3.7,0,5.5,1.4,7.4,2.9c1.9,1.5,3.9,3.1,7.9,3.1
|
||||
c4,0,5.9-1.6,7.9-3.1c1.9-1.5,3.7-2.9,7.4-2.9c3.7,0,5.5,1.4,7.4,2.9c1.9,1.5,3.9,3.1,7.9,3.1c4,0,5.9-1.6,7.9-3.1
|
||||
c1.9-1.5,3.7-2.9,7.4-2.9c3.7,0,5.5,1.4,7.4,2.9c1.3,1,2.5,2,4.4,2.6c-0.1-0.3-0.3-0.7-0.4-1c-1.3-0.6-2.4-1.4-3.4-2.2
|
||||
c-1.9-1.5-3.9-3.1-7.9-3.1c-4,0-5.9,1.6-7.9,3.1c-1.9,1.5-3.7,2.9-7.4,2.9c-3.7,0-5.5-1.4-7.4-2.9c-1.9-1.5-3.9-3.1-7.9-3.1
|
||||
c-4,0-5.9,1.6-7.9,3.1c-1.9,1.5-3.7,2.9-7.4,2.9c-3.7,0-5.5-1.4-7.4-2.9C62,51.2,60,49.6,56,49.6c-4,0-5.9,1.6-7.9,3.1
|
||||
c-1.9,1.5-3.7,2.9-7.4,2.9c-3.7,0-5.5-1.4-7.4-2.9C33,52.4,32.5,52,32,51.6c-0.1,0.2-0.3,0.5-0.4,0.8c0.4,0.3,0.9,0.6,1.3,1
|
||||
C34.8,54.9,36.8,56.5,40.8,56.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 34 KiB |
@@ -1,105 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 431.3 150" style="enable-background:new 0 0 431.3 150;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:none;}
|
||||
.st2{fill:#009BDB;}
|
||||
.st3{fill:#717074;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M196.6,55.8l-18.2,41.2h-5.1l-18.2-41.2h5.1l15.7,35.5l15.6-35.5H196.6z"/>
|
||||
<path class="st0" d="M206.6,60.3v13.6h22.4v4.4h-22.4v14.3h24.8v4.4h-29.6V55.8h29.6v4.4H206.6z"/>
|
||||
<path class="st0" d="M265.7,92.6v4.4h-27.2V55.8h4.7v36.8H265.7z"/>
|
||||
<path class="st0" d="M275.7,60.3v13.6h22.4v4.4h-22.4v14.3h24.8v4.4H271V55.8h29.6v4.4H275.7z"/>
|
||||
<path class="st0" d="M338,75.3c-1.1,1.9-2.6,3.4-4.6,4.6c-2,1.2-4.2,1.9-6.6,2.2l10.5,14.9H332l-10.4-14.8h-9.1v14.8h-4.7V55.8
|
||||
h16.7c2.8,0,5.4,0.6,7.7,1.7c2.3,1.1,4.1,2.7,5.5,4.7c1.3,2,2,4.3,2,6.8C339.6,71.3,339.1,73.4,338,75.3z M312.4,77.8h11.2
|
||||
c3.4,0,6.1-0.8,8.2-2.3c2.1-1.6,3.1-3.7,3.1-6.4c0-2.7-1-4.9-3.1-6.4c-2.1-1.6-4.8-2.3-8.2-2.3h-11.2V77.8z"/>
|
||||
<path class="st0" d="M354.4,94.9c-3.3-1.9-5.8-4.5-7.8-7.8c-1.9-3.3-2.9-6.8-2.9-10.6c0-3.8,1-7.3,2.9-10.6
|
||||
c1.9-3.3,4.5-5.9,7.8-7.8c3.3-1.9,6.8-2.9,10.5-2.9c3.8,0,7.2,1,10.5,2.9c3.2,1.9,5.8,4.5,7.7,7.8c1.9,3.3,2.9,6.8,2.9,10.6
|
||||
c0,3.8-1,7.3-2.9,10.6c-1.9,3.3-4.5,5.9-7.7,7.8c-3.2,1.9-6.7,2.9-10.5,2.9C361.2,97.8,357.7,96.8,354.4,94.9z M373,91.1
|
||||
c2.5-1.6,4.5-3.6,6-6.2c1.5-2.6,2.2-5.4,2.2-8.5c0-3-0.7-5.8-2.2-8.4c-1.5-2.6-3.5-4.7-6-6.2c-2.5-1.5-5.2-2.3-8.1-2.3
|
||||
c-2.9,0-5.6,0.8-8.1,2.3c-2.5,1.5-4.5,3.6-6,6.2c-1.5,2.6-2.2,5.4-2.2,8.4c0,3,0.8,5.9,2.2,8.5c1.5,2.6,3.5,4.7,6,6.2
|
||||
s5.2,2.3,8.1,2.3C367.8,93.5,370.5,92.7,373,91.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M132.2,77.1c0-0.7,0.1-1.4,0.1-2s0-1.4-0.1-2V77.1z"/>
|
||||
<path class="st2" d="M117,109.3c-3.3,0-4.9,1.2-6.7,2.7c-2,1.6-4.2,3.4-8.6,3.4c-4.3,0-6.6-1.8-8.6-3.4c-1.8-1.4-3.4-2.7-6.7-2.7
|
||||
c-3.3,0-4.9,1.2-6.7,2.7c-2,1.6-4.2,3.4-8.6,3.4c-4.3,0-6.6-1.8-8.6-3.4c-1.8-1.4-3.4-2.7-6.7-2.7c-3.3,0-4.9,1.2-6.7,2.7
|
||||
c-1.3,1-2.7,2.2-4.8,2.8c8.3,7.3,18.9,12,30.5,13c0.3,0,0.6,0.1,1,0.1c1.1,0.1,2.3,0.1,3.4,0.1c1.2,0,2.3-0.1,3.4-0.1
|
||||
c0.3,0,0.6,0,1-0.1c14.2-1.2,26.8-8,35.6-18.2C118.7,109.4,117.9,109.3,117,109.3z"/>
|
||||
<path class="st2" d="M40.8,69.8c3.6,0,5.3-1.4,7.2-2.9c2-1.6,4-3.2,8-3.2s6.1,1.6,8,3.2c1.9,1.5,3.6,2.9,7.2,2.9
|
||||
c3.6,0,5.3-1.4,7.2-2.9c2-1.6,4-3.2,8-3.2c4,0,6.1,1.6,8,3.2c1.9,1.5,3.6,2.9,7.2,2.9c3.6,0,5.3-1.4,7.2-2.9c2-1.6,4-3.2,8-3.2
|
||||
c4,0,6.1,1.6,8,3.2c1.8,1.5,3.6,2.8,7,2.9c-0.5-4.8-1.6-9.5-3.3-13.8c-1.8-0.6-3.1-1.6-4.4-2.6c-1.9-1.5-3.7-2.9-7.4-2.9
|
||||
c-3.7,0-5.5,1.4-7.4,2.9c-1.9,1.5-3.9,3.1-7.9,3.1c-4,0-5.9-1.6-7.9-3.1c-1.9-1.5-3.7-2.9-7.4-2.9c-3.7,0-5.5,1.4-7.4,2.9
|
||||
c-1.9,1.5-3.9,3.1-7.9,3.1c-4,0-5.9-1.6-7.9-3.1c-1.9-1.5-3.7-2.9-7.4-2.9c-3.7,0-5.5,1.4-7.4,2.9c-1.9,1.5-3.9,3.1-7.9,3.1
|
||||
c-4,0-5.9-1.6-7.9-3.1c-0.4-0.3-0.9-0.7-1.3-1c-1.7,3.6-3.1,7.5-3.9,11.6c2.7,0.5,4.3,1.7,5.9,3C35.5,68.4,37.2,69.8,40.8,69.8z"
|
||||
/>
|
||||
<path class="st2" d="M40.8,55.7c3.7,0,5.5-1.4,7.4-2.9c1.9-1.5,3.9-3.1,7.9-3.1c4,0,5.9,1.6,7.9,3.1c1.9,1.5,3.7,2.9,7.4,2.9
|
||||
c3.7,0,5.5-1.4,7.4-2.9c1.9-1.5,3.9-3.1,7.9-3.1c4,0,5.9,1.6,7.9,3.1c1.9,1.5,3.7,2.9,7.4,2.9c3.7,0,5.5-1.4,7.4-2.9
|
||||
c1.9-1.5,3.9-3.1,7.9-3.1c4,0,5.9,1.6,7.9,3.1c1.1,0.8,2.1,1.7,3.4,2.2c-7.9-19.2-26.9-32.8-48.9-32.8c-20.8,0-38.7,12-47.4,29.5
|
||||
c0.5,0.4,1,0.7,1.4,1.1C35.3,54.2,37.1,55.7,40.8,55.7z"/>
|
||||
<path class="st2" d="M117,94.6c-3.4,0-5,1.3-6.8,2.8c-1.9,1.5-4.1,3.3-8.4,3.3c-4.2,0-6.4-1.7-8.4-3.3c-1.9-1.5-3.5-2.8-6.8-2.8
|
||||
c-3.4,0-5,1.3-6.8,2.8c-1.9,1.5-4.1,3.3-8.4,3.3c-4.2,0-6.4-1.7-8.4-3.3c-1.9-1.5-3.5-2.8-6.8-2.8c-3.4,0-5,1.3-6.8,2.8
|
||||
c-1.9,1.5-4.1,3.3-8.4,3.3c-4.2,0-6.4-1.7-8.4-3.3c-0.5-0.4-1-0.8-1.5-1.1c2.6,6,6.3,11.4,10.9,16c2.6-0.2,4-1.4,5.6-2.6
|
||||
c2-1.6,4.2-3.4,8.6-3.4s6.6,1.8,8.6,3.4c1.8,1.4,3.4,2.7,6.7,2.7c3.3,0,4.9-1.2,6.7-2.7c2-1.6,4.2-3.4,8.6-3.4
|
||||
c4.3,0,6.6,1.8,8.6,3.4c1.8,1.4,3.4,2.7,6.7,2.7c3.3,0,4.9-1.2,6.7-2.7c2-1.6,4.2-3.4,8.6-3.4c1.8,0,3.3,0.3,4.5,0.8
|
||||
c1.9-2.5,3.5-5.1,5-7.9c-1-0.6-1.8-1.2-2.6-1.8C122,95.8,120.3,94.6,117,94.6z"/>
|
||||
<path class="st2" d="M132.1,71.2c-4,0-6-1.6-8-3.2c-1.9-1.5-3.6-2.9-7.2-2.9c-3.6,0-5.3,1.4-7.2,2.9c-2,1.6-4,3.2-8,3.2
|
||||
c-4,0-6.1-1.6-8-3.2c-1.9-1.5-3.6-2.9-7.2-2.9c-3.6,0-5.3,1.4-7.2,2.9c-2,1.6-4,3.2-8,3.2c-4,0-6.1-1.6-8-3.2
|
||||
c-1.9-1.5-3.6-2.9-7.2-2.9c-3.6,0-5.3,1.4-7.2,2.9c-2,1.6-4,3.2-8,3.2c-4,0-6.1-1.6-8-3.2c-1.5-1.2-3-2.3-5.3-2.7
|
||||
c-0.5,2.5-0.8,5.1-0.9,7.7c0,0.7-0.1,1.4-0.1,2c0,0.7,0,1.4,0.1,2c0,0.3,0,0.6,0,0.9c3.5,0.3,5.4,1.8,7.2,3.2
|
||||
c1.8,1.4,3.5,2.8,7,2.8c3.5,0,5.2-1.4,7-2.8c1.9-1.5,4.1-3.2,8.2-3.2s6.3,1.7,8.2,3.2c1.8,1.4,3.5,2.8,7,2.8c3.5,0,5.2-1.4,7-2.8
|
||||
c1.9-1.5,4.1-3.2,8.2-3.2c4.1,0,6.3,1.7,8.2,3.2c1.8,1.4,3.5,2.8,7,2.8c3.5,0,5.2-1.4,7-2.8c1.9-1.5,4.1-3.2,8.2-3.2
|
||||
c4.1,0,6.3,1.7,8.2,3.2c1.7,1.3,3.3,2.6,6.3,2.8c0.3-1.6,0.5-3.2,0.6-4.9c0-0.6,0.1-1.3,0.1-1.9V73
|
||||
C132.2,72.4,132.1,71.2,132.1,71.2z"/>
|
||||
<path class="st2" d="M117,79.9c-3.5,0-5.2,1.4-7,2.8c-1.9,1.5-4.1,3.2-8.2,3.2c-4.1,0-6.3-1.7-8.2-3.2c-1.8-1.4-3.5-2.8-7-2.8
|
||||
c-3.5,0-5.2,1.4-7,2.8c-1.9,1.5-4.1,3.2-8.2,3.2c-4.1,0-6.3-1.7-8.2-3.2c-1.8-1.4-3.5-2.8-7-2.8c-3.5,0-5.2,1.4-7,2.8
|
||||
c-1.9,1.5-4.1,3.2-8.2,3.2c-4.1,0-6.3-1.7-8.2-3.2c-1.6-1.3-3.1-2.5-5.8-2.8c0.4,4.5,1.4,8.7,2.8,12.8c1.9,0.6,3.2,1.7,4.4,2.7
|
||||
c1.9,1.5,3.5,2.8,6.8,2.8c3.4,0,5-1.3,6.8-2.8c1.9-1.5,4.1-3.3,8.4-3.3c4.2,0,6.4,1.7,8.4,3.3c1.9,1.5,3.5,2.8,6.8,2.8
|
||||
c3.4,0,5-1.3,6.8-2.8c1.9-1.5,4.1-3.3,8.4-3.3c4.2,0,6.4,1.7,8.4,3.3c1.9,1.5,3.5,2.8,6.8,2.8c3.4,0,5-1.3,6.8-2.8
|
||||
c1.9-1.5,4.1-3.3,8.4-3.3c4.2,0,6.4,1.7,8.4,3.3c0.7,0.6,1.4,1.1,2.2,1.6c1.6-3.5,2.8-7.2,3.6-11.1c-3.5-0.3-5.4-1.8-7.2-3.2
|
||||
C122.2,81.2,120.4,79.9,117,79.9z"/>
|
||||
<path class="st0" d="M108.4,109.6c-1.8,1.4-3.4,2.7-6.7,2.7c-3.3,0-4.9-1.2-6.7-2.7c-2-1.6-4.2-3.4-8.6-3.4
|
||||
c-4.3,0-6.6,1.8-8.6,3.4c-1.8,1.4-3.4,2.7-6.7,2.7c-3.3,0-4.9-1.2-6.7-2.7c-2-1.6-4.2-3.4-8.6-3.4s-6.6,1.8-8.6,3.4
|
||||
c-1.6,1.3-3,2.4-5.6,2.6c0.9,0.9,1.8,1.7,2.7,2.5c2.1-0.6,3.5-1.8,4.8-2.8c1.8-1.4,3.4-2.7,6.7-2.7c3.3,0,4.9,1.2,6.7,2.7
|
||||
c2,1.6,4.2,3.4,8.6,3.4c4.3,0,6.6-1.8,8.6-3.4c1.8-1.4,3.4-2.7,6.7-2.7c3.3,0,4.9,1.2,6.7,2.7c2,1.6,4.2,3.4,8.6,3.4
|
||||
c4.3,0,6.6-1.8,8.6-3.4c1.8-1.4,3.4-2.7,6.7-2.7c0.9,0,1.7,0.1,2.4,0.3c0.7-0.8,1.4-1.7,2-2.5c-1.2-0.5-2.7-0.8-4.5-0.8
|
||||
C112.6,106.2,110.4,108,108.4,109.6z"/>
|
||||
<path class="st0" d="M117,92.1c-4.2,0-6.4,1.7-8.4,3.3c-1.9,1.5-3.5,2.8-6.8,2.8c-3.4,0-5-1.3-6.8-2.8c-1.9-1.5-4.1-3.3-8.4-3.3
|
||||
c-4.2,0-6.4,1.7-8.4,3.3c-1.9,1.5-3.5,2.8-6.8,2.8c-3.4,0-5-1.3-6.8-2.8c-1.9-1.5-4.1-3.3-8.4-3.3c-4.2,0-6.4,1.7-8.4,3.3
|
||||
c-1.9,1.5-3.5,2.8-6.8,2.8c-3.4,0-5-1.3-6.8-2.8c-1.2-1-2.5-2-4.4-2.7c0.4,1.2,0.9,2.3,1.4,3.5c0.5,0.3,1,0.7,1.5,1.1
|
||||
c1.9,1.5,4.1,3.3,8.4,3.3c4.2,0,6.4-1.7,8.4-3.3c1.9-1.5,3.5-2.8,6.8-2.8c3.4,0,5,1.3,6.8,2.8c1.9,1.5,4.1,3.3,8.4,3.3
|
||||
c4.2,0,6.4-1.7,8.4-3.3c1.9-1.5,3.5-2.8,6.8-2.8c3.4,0,5,1.3,6.8,2.8c1.9,1.5,4.1,3.3,8.4,3.3c4.2,0,6.4-1.7,8.4-3.3
|
||||
c1.9-1.5,3.5-2.8,6.8-2.8c3.4,0,5,1.3,6.8,2.8c0.8,0.6,1.6,1.3,2.6,1.8c0.4-0.7,0.7-1.5,1.1-2.2c-0.8-0.4-1.4-1-2.2-1.6
|
||||
C123.4,93.8,121.2,92.1,117,92.1z"/>
|
||||
<path class="st0" d="M117,77.9c-4.1,0-6.3,1.7-8.2,3.2c-1.8,1.4-3.5,2.8-7,2.8c-3.5,0-5.2-1.4-7-2.8c-1.9-1.5-4.1-3.2-8.2-3.2
|
||||
c-4.1,0-6.3,1.7-8.2,3.2c-1.8,1.4-3.5,2.8-7,2.8c-3.5,0-5.2-1.4-7-2.8c-1.9-1.5-4.1-3.2-8.2-3.2c-4.1,0-6.3,1.7-8.2,3.2
|
||||
c-1.8,1.4-3.5,2.8-7,2.8c-3.5,0-5.2-1.4-7-2.8c-1.7-1.4-3.7-2.9-7.2-3.2c0,0.6,0.1,1.3,0.1,1.9c2.7,0.3,4.2,1.5,5.8,2.8
|
||||
c1.9,1.5,4.1,3.2,8.2,3.2c4.1,0,6.3-1.7,8.2-3.2c1.8-1.4,3.5-2.8,7-2.8c3.5,0,5.2,1.4,7,2.8c1.9,1.5,4.1,3.2,8.2,3.2
|
||||
c4.1,0,6.3-1.7,8.2-3.2c1.8-1.4,3.5-2.8,7-2.8c3.5,0,5.2,1.4,7,2.8c1.9,1.5,4.1,3.2,8.2,3.2c4.1,0,6.3-1.7,8.2-3.2
|
||||
c1.8-1.4,3.5-2.8,7-2.8c3.5,0,5.2,1.4,7,2.8c1.7,1.4,3.7,2.9,7.2,3.2c0.1-0.6,0.2-1.3,0.3-1.9c-3-0.2-4.6-1.4-6.3-2.8
|
||||
C123.3,79.6,121.1,77.9,117,77.9z"/>
|
||||
<path class="st0" d="M40.8,71.2c4,0,6.1-1.6,8-3.2c1.9-1.5,3.6-2.9,7.2-2.9c3.6,0,5.3,1.4,7.2,2.9c2,1.6,4,3.2,8,3.2
|
||||
c4,0,6.1-1.6,8-3.2c1.9-1.5,3.6-2.9,7.2-2.9c3.6,0,5.3,1.4,7.2,2.9c2,1.6,4,3.2,8,3.2c4,0,6.1-1.6,8-3.2c1.9-1.5,3.6-2.9,7.2-2.9
|
||||
c3.6,0,5.3,1.4,7.2,2.9c1.9,1.5,4,3.1,8,3.2l0-0.1c0-0.4-0.1-0.8-0.1-1.3c-3.4-0.1-5.2-1.4-7-2.9c-2-1.6-4-3.2-8-3.2
|
||||
c-4,0-6.1,1.6-8,3.2c-1.9,1.5-3.6,2.9-7.2,2.9c-3.6,0-5.3-1.4-7.2-2.9c-2-1.6-4-3.2-8-3.2c-4,0-6.1,1.6-8,3.2
|
||||
c-1.9,1.5-3.6,2.9-7.2,2.9c-3.6,0-5.3-1.4-7.2-2.9c-2-1.6-4-3.2-8-3.2s-6.1,1.6-8,3.2c-1.9,1.5-3.6,2.9-7.2,2.9
|
||||
c-3.6,0-5.3-1.4-7.2-2.9c-1.6-1.3-3.2-2.5-5.9-3c-0.1,0.4-0.2,0.9-0.3,1.3c2.4,0.4,3.8,1.5,5.3,2.7
|
||||
C34.7,69.6,36.7,71.2,40.8,71.2z"/>
|
||||
<path class="st0" d="M40.8,56.5c4,0,5.9-1.6,7.9-3.1c1.9-1.5,3.7-2.9,7.4-2.9c3.7,0,5.5,1.4,7.4,2.9c1.9,1.5,3.9,3.1,7.9,3.1
|
||||
c4,0,5.9-1.6,7.9-3.1c1.9-1.5,3.7-2.9,7.4-2.9c3.7,0,5.5,1.4,7.4,2.9c1.9,1.5,3.9,3.1,7.9,3.1c4,0,5.9-1.6,7.9-3.1
|
||||
c1.9-1.5,3.7-2.9,7.4-2.9c3.7,0,5.5,1.4,7.4,2.9c1.3,1,2.5,2,4.4,2.6c-0.1-0.3-0.3-0.7-0.4-1c-1.3-0.6-2.4-1.4-3.4-2.2
|
||||
c-1.9-1.5-3.9-3.1-7.9-3.1c-4,0-5.9,1.6-7.9,3.1c-1.9,1.5-3.7,2.9-7.4,2.9c-3.7,0-5.5-1.4-7.4-2.9c-1.9-1.5-3.9-3.1-7.9-3.1
|
||||
c-4,0-5.9,1.6-7.9,3.1c-1.9,1.5-3.7,2.9-7.4,2.9c-3.7,0-5.5-1.4-7.4-2.9C62,51.2,60,49.6,56,49.6c-4,0-5.9,1.6-7.9,3.1
|
||||
c-1.9,1.5-3.7,2.9-7.4,2.9c-3.7,0-5.5-1.4-7.4-2.9C33,52.4,32.5,52,32,51.6c-0.1,0.2-0.3,0.5-0.4,0.8c0.4,0.3,0.9,0.6,1.3,1
|
||||
C34.8,54.9,36.8,56.5,40.8,56.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 34 KiB |
@@ -1,103 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 311.5 245.2" style="enable-background:new 0 0 311.5 245.2;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:none;}
|
||||
.st2{fill:#009BDB;}
|
||||
.st3{fill:#717074;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M211.5,83.4c0-0.7,0.1-1.4,0.1-2c0-0.7,0-1.4-0.1-2V83.4z"/>
|
||||
<path class="st2" d="M196.3,115.5c-3.3,0-4.9,1.2-6.7,2.7c-2,1.6-4.2,3.4-8.6,3.4c-4.3,0-6.6-1.8-8.6-3.4
|
||||
c-1.8-1.4-3.4-2.7-6.7-2.7c-3.3,0-4.9,1.2-6.7,2.7c-2,1.6-4.2,3.4-8.6,3.4c-4.3,0-6.6-1.8-8.6-3.4c-1.8-1.4-3.4-2.7-6.7-2.7
|
||||
s-4.9,1.2-6.7,2.7c-1.3,1-2.7,2.2-4.8,2.8c8.3,7.3,18.9,12,30.5,13c0.3,0,0.6,0.1,1,0.1c1.1,0.1,2.3,0.1,3.4,0.1
|
||||
c1.2,0,2.3-0.1,3.4-0.1c0.3,0,0.6,0,1-0.1c14.2-1.2,26.8-8,35.6-18.2C198,115.7,197.2,115.5,196.3,115.5z"/>
|
||||
<path class="st2" d="M120.1,76.1c3.6,0,5.3-1.4,7.2-2.9c2-1.6,4-3.2,8-3.2c4,0,6.1,1.6,8,3.2c1.9,1.5,3.6,2.9,7.2,2.9
|
||||
c3.6,0,5.3-1.4,7.2-2.9c2-1.6,4-3.2,8-3.2c4,0,6.1,1.6,8,3.2c1.9,1.5,3.6,2.9,7.2,2.9c3.6,0,5.3-1.4,7.2-2.9c2-1.6,4-3.2,8-3.2
|
||||
c4,0,6.1,1.6,8,3.2c1.8,1.5,3.6,2.8,7,2.9c-0.5-4.8-1.6-9.5-3.3-13.8c-1.8-0.6-3.1-1.6-4.4-2.6c-1.9-1.5-3.7-2.9-7.4-2.9
|
||||
c-3.7,0-5.5,1.4-7.4,2.9c-1.9,1.5-3.9,3.1-7.9,3.1c-4,0-5.9-1.6-7.9-3.1c-1.9-1.5-3.7-2.9-7.4-2.9c-3.7,0-5.5,1.4-7.4,2.9
|
||||
c-1.9,1.5-3.9,3.1-7.9,3.1c-4,0-5.9-1.6-7.9-3.1c-1.9-1.5-3.7-2.9-7.4-2.9s-5.5,1.4-7.4,2.9c-1.9,1.5-3.9,3.1-7.9,3.1
|
||||
c-4,0-5.9-1.6-7.9-3.1c-0.4-0.3-0.9-0.7-1.3-1c-1.7,3.6-3.1,7.5-3.9,11.6c2.7,0.5,4.3,1.7,5.9,3C114.8,74.7,116.5,76.1,120.1,76.1
|
||||
z"/>
|
||||
<path class="st2" d="M120.1,61.9c3.7,0,5.5-1.4,7.4-2.9c1.9-1.5,3.9-3.1,7.9-3.1c4,0,5.9,1.6,7.9,3.1c1.9,1.5,3.7,2.9,7.4,2.9
|
||||
c3.7,0,5.5-1.4,7.4-2.9c1.9-1.5,3.9-3.1,7.9-3.1c4,0,5.9,1.6,7.9,3.1c1.9,1.5,3.7,2.9,7.4,2.9c3.7,0,5.5-1.4,7.4-2.9
|
||||
c1.9-1.5,3.9-3.1,7.9-3.1c4,0,5.9,1.6,7.9,3.1c1.1,0.8,2.1,1.7,3.4,2.2c-7.9-19.2-26.9-32.8-48.9-32.8c-20.8,0-38.7,12-47.4,29.5
|
||||
c0.5,0.4,1,0.7,1.4,1.1C114.6,60.5,116.4,61.9,120.1,61.9z"/>
|
||||
<path class="st2" d="M196.3,100.8c-3.4,0-5,1.3-6.8,2.8c-1.9,1.5-4.1,3.3-8.4,3.3c-4.2,0-6.4-1.7-8.4-3.3
|
||||
c-1.9-1.5-3.5-2.8-6.8-2.8c-3.4,0-5,1.3-6.8,2.8c-1.9,1.5-4.1,3.3-8.4,3.3c-4.2,0-6.4-1.7-8.4-3.3c-1.9-1.5-3.5-2.8-6.8-2.8
|
||||
c-3.4,0-5,1.3-6.8,2.8c-1.9,1.5-4.1,3.3-8.4,3.3c-4.2,0-6.4-1.7-8.4-3.3c-0.5-0.4-1-0.8-1.5-1.1c2.6,6,6.3,11.4,10.9,16
|
||||
c2.6-0.2,4-1.4,5.6-2.6c2-1.6,4.2-3.4,8.6-3.4c4.3,0,6.6,1.8,8.6,3.4c1.8,1.4,3.4,2.7,6.7,2.7c3.3,0,4.9-1.2,6.7-2.7
|
||||
c2-1.6,4.2-3.4,8.6-3.4c4.3,0,6.6,1.8,8.6,3.4c1.8,1.4,3.4,2.7,6.7,2.7c3.3,0,4.9-1.2,6.7-2.7c2-1.6,4.2-3.4,8.6-3.4
|
||||
c1.8,0,3.3,0.3,4.5,0.8c1.9-2.5,3.5-5.1,5-7.9c-1-0.6-1.8-1.2-2.6-1.8C201.3,102.1,199.6,100.8,196.3,100.8z"/>
|
||||
<path class="st2" d="M211.4,77.5c-4,0-6-1.6-8-3.2c-1.9-1.5-3.6-2.9-7.2-2.9c-3.6,0-5.3,1.4-7.2,2.9c-2,1.6-4,3.2-8,3.2
|
||||
c-4,0-6.1-1.6-8-3.2c-1.9-1.5-3.6-2.9-7.2-2.9c-3.6,0-5.3,1.4-7.2,2.9c-2,1.6-4,3.2-8,3.2c-4,0-6.1-1.6-8-3.2
|
||||
c-1.9-1.5-3.6-2.9-7.2-2.9c-3.6,0-5.3,1.4-7.2,2.9c-2,1.6-4,3.2-8,3.2c-4,0-6.1-1.6-8-3.2c-1.5-1.2-3-2.3-5.3-2.7
|
||||
c-0.5,2.5-0.8,5.1-0.9,7.7c0,0.7-0.1,1.4-0.1,2c0,0.7,0,1.4,0.1,2c0,0.3,0,0.6,0,0.9c3.5,0.3,5.4,1.8,7.2,3.2
|
||||
c1.8,1.4,3.5,2.8,7,2.8c3.5,0,5.2-1.4,7-2.8c1.9-1.5,4.1-3.2,8.2-3.2s6.3,1.7,8.2,3.2c1.8,1.4,3.5,2.8,7,2.8c3.5,0,5.2-1.4,7-2.8
|
||||
c1.9-1.5,4.1-3.2,8.2-3.2c4.1,0,6.3,1.7,8.2,3.2c1.8,1.4,3.5,2.8,7,2.8c3.5,0,5.2-1.4,7-2.8c1.9-1.5,4.1-3.2,8.2-3.2
|
||||
c4.1,0,6.3,1.7,8.2,3.2c1.7,1.3,3.3,2.6,6.3,2.8c0.3-1.6,0.5-3.2,0.6-4.9c0-0.6,0.1-1.3,0.1-1.9v-4.1
|
||||
C211.5,78.6,211.4,77.5,211.4,77.5z"/>
|
||||
<path class="st2" d="M196.3,86.1c-3.5,0-5.2,1.4-7,2.8c-1.9,1.5-4.1,3.2-8.2,3.2c-4.1,0-6.3-1.7-8.2-3.2c-1.8-1.4-3.5-2.8-7-2.8
|
||||
c-3.5,0-5.2,1.4-7,2.8c-1.9,1.5-4.1,3.2-8.2,3.2c-4.1,0-6.3-1.7-8.2-3.2c-1.8-1.4-3.5-2.8-7-2.8c-3.5,0-5.2,1.4-7,2.8
|
||||
c-1.9,1.5-4.1,3.2-8.2,3.2c-4.1,0-6.3-1.7-8.2-3.2c-1.6-1.3-3.1-2.5-5.8-2.8c0.4,4.5,1.4,8.7,2.8,12.8c1.9,0.6,3.2,1.7,4.4,2.7
|
||||
c1.9,1.5,3.5,2.8,6.8,2.8c3.4,0,5-1.3,6.8-2.8c1.9-1.5,4.1-3.3,8.4-3.3c4.2,0,6.4,1.7,8.4,3.3c1.9,1.5,3.5,2.8,6.8,2.8
|
||||
c3.4,0,5-1.3,6.8-2.8c1.9-1.5,4.1-3.3,8.4-3.3c4.2,0,6.4,1.7,8.4,3.3c1.9,1.5,3.5,2.8,6.8,2.8c3.4,0,5-1.3,6.8-2.8
|
||||
c1.9-1.5,4.1-3.3,8.4-3.3c4.2,0,6.4,1.7,8.4,3.3c0.7,0.6,1.4,1.1,2.2,1.6c1.6-3.5,2.8-7.2,3.6-11.1c-3.5-0.3-5.4-1.8-7.2-3.2
|
||||
C201.5,87.5,199.7,86.1,196.3,86.1z"/>
|
||||
<path class="st0" d="M187.7,115.9c-1.8,1.4-3.4,2.7-6.7,2.7c-3.3,0-4.9-1.2-6.7-2.7c-2-1.6-4.2-3.4-8.6-3.4
|
||||
c-4.3,0-6.6,1.8-8.6,3.4c-1.8,1.4-3.4,2.7-6.7,2.7c-3.3,0-4.9-1.2-6.7-2.7c-2-1.6-4.2-3.4-8.6-3.4c-4.3,0-6.6,1.8-8.6,3.4
|
||||
c-1.6,1.3-3,2.4-5.6,2.6c0.9,0.9,1.8,1.7,2.7,2.5c2.1-0.6,3.5-1.8,4.8-2.8c1.8-1.4,3.4-2.7,6.7-2.7c3.3,0,4.9,1.2,6.7,2.7
|
||||
c2,1.6,4.2,3.4,8.6,3.4c4.3,0,6.6-1.8,8.6-3.4c1.8-1.4,3.4-2.7,6.7-2.7c3.3,0,4.9,1.2,6.7,2.7c2,1.6,4.2,3.4,8.6,3.4
|
||||
c4.3,0,6.6-1.8,8.6-3.4c1.8-1.4,3.4-2.7,6.7-2.7c0.9,0,1.7,0.1,2.4,0.3c0.7-0.8,1.4-1.7,2-2.5c-1.2-0.5-2.7-0.8-4.5-0.8
|
||||
C191.9,112.5,189.7,114.3,187.7,115.9z"/>
|
||||
<path class="st0" d="M196.3,98.4c-4.2,0-6.4,1.7-8.4,3.3c-1.9,1.5-3.5,2.8-6.8,2.8c-3.4,0-5-1.3-6.8-2.8c-1.9-1.5-4.1-3.3-8.4-3.3
|
||||
c-4.2,0-6.4,1.7-8.4,3.3c-1.9,1.5-3.5,2.8-6.8,2.8c-3.4,0-5-1.3-6.8-2.8c-1.9-1.5-4.1-3.3-8.4-3.3c-4.2,0-6.4,1.7-8.4,3.3
|
||||
c-1.9,1.5-3.5,2.8-6.8,2.8c-3.4,0-5-1.3-6.8-2.8c-1.2-1-2.5-2-4.4-2.7c0.4,1.2,0.9,2.3,1.4,3.5c0.5,0.3,1,0.7,1.5,1.1
|
||||
c1.9,1.5,4.1,3.3,8.4,3.3c4.2,0,6.4-1.7,8.4-3.3c1.9-1.5,3.5-2.8,6.8-2.8c3.4,0,5,1.3,6.8,2.8c1.9,1.5,4.1,3.3,8.4,3.3
|
||||
c4.2,0,6.4-1.7,8.4-3.3c1.9-1.5,3.5-2.8,6.8-2.8c3.4,0,5,1.3,6.8,2.8c1.9,1.5,4.1,3.3,8.4,3.3c4.2,0,6.4-1.7,8.4-3.3
|
||||
c1.9-1.5,3.5-2.8,6.8-2.8c3.4,0,5,1.3,6.8,2.8c0.8,0.6,1.6,1.3,2.6,1.8c0.4-0.7,0.7-1.5,1.1-2.2c-0.8-0.4-1.4-1-2.2-1.6
|
||||
C202.7,100.1,200.5,98.4,196.3,98.4z"/>
|
||||
<path class="st0" d="M196.3,84.2c-4.1,0-6.3,1.7-8.2,3.2c-1.8,1.4-3.5,2.8-7,2.8c-3.5,0-5.2-1.4-7-2.8c-1.9-1.5-4.1-3.2-8.2-3.2
|
||||
c-4.1,0-6.3,1.7-8.2,3.2c-1.8,1.4-3.5,2.8-7,2.8c-3.5,0-5.2-1.4-7-2.8c-1.9-1.5-4.1-3.2-8.2-3.2c-4.1,0-6.3,1.7-8.2,3.2
|
||||
c-1.8,1.4-3.5,2.8-7,2.8c-3.5,0-5.2-1.4-7-2.8c-1.7-1.4-3.7-2.9-7.2-3.2c0,0.6,0.1,1.3,0.1,1.9c2.7,0.3,4.2,1.5,5.8,2.8
|
||||
c1.9,1.5,4.1,3.2,8.2,3.2c4.1,0,6.3-1.7,8.2-3.2c1.8-1.4,3.5-2.8,7-2.8c3.5,0,5.2,1.4,7,2.8c1.9,1.5,4.1,3.2,8.2,3.2
|
||||
c4.1,0,6.3-1.7,8.2-3.2c1.8-1.4,3.5-2.8,7-2.8c3.5,0,5.2,1.4,7,2.8c1.9,1.5,4.1,3.2,8.2,3.2c4.1,0,6.3-1.7,8.2-3.2
|
||||
c1.8-1.4,3.5-2.8,7-2.8c3.5,0,5.2,1.4,7,2.8c1.7,1.4,3.7,2.9,7.2,3.2c0.1-0.6,0.2-1.3,0.3-1.9c-3-0.2-4.6-1.4-6.3-2.8
|
||||
C202.6,85.9,200.4,84.2,196.3,84.2z"/>
|
||||
<path class="st0" d="M120.1,77.5c4,0,6.1-1.6,8-3.2c1.9-1.5,3.6-2.9,7.2-2.9c3.6,0,5.3,1.4,7.2,2.9c2,1.6,4,3.2,8,3.2
|
||||
c4,0,6.1-1.6,8-3.2c1.9-1.5,3.6-2.9,7.2-2.9c3.6,0,5.3,1.4,7.2,2.9c2,1.6,4,3.2,8,3.2c4,0,6.1-1.6,8-3.2c1.9-1.5,3.6-2.9,7.2-2.9
|
||||
c3.6,0,5.3,1.4,7.2,2.9c1.9,1.5,4,3.1,8,3.2l0-0.1c0-0.4-0.1-0.8-0.1-1.3c-3.4-0.1-5.2-1.4-7-2.9c-2-1.6-4-3.2-8-3.2
|
||||
c-4,0-6.1,1.6-8,3.2c-1.9,1.5-3.6,2.9-7.2,2.9c-3.6,0-5.3-1.4-7.2-2.9c-2-1.6-4-3.2-8-3.2c-4,0-6.1,1.6-8,3.2
|
||||
c-1.9,1.5-3.6,2.9-7.2,2.9c-3.6,0-5.3-1.4-7.2-2.9c-2-1.6-4-3.2-8-3.2c-4,0-6.1,1.6-8,3.2c-1.9,1.5-3.6,2.9-7.2,2.9
|
||||
c-3.6,0-5.3-1.4-7.2-2.9c-1.6-1.3-3.2-2.5-5.9-3c-0.1,0.4-0.2,0.9-0.3,1.3c2.4,0.4,3.8,1.5,5.3,2.7C114,75.9,116,77.5,120.1,77.5z
|
||||
"/>
|
||||
<path class="st0" d="M120.1,62.8c4,0,5.9-1.6,7.9-3.1c1.9-1.5,3.7-2.9,7.4-2.9s5.5,1.4,7.4,2.9c1.9,1.5,3.9,3.1,7.9,3.1
|
||||
c4,0,5.9-1.6,7.9-3.1c1.9-1.5,3.7-2.9,7.4-2.9c3.7,0,5.5,1.4,7.4,2.9c1.9,1.5,3.9,3.1,7.9,3.1c4,0,5.9-1.6,7.9-3.1
|
||||
c1.9-1.5,3.7-2.9,7.4-2.9c3.7,0,5.5,1.4,7.4,2.9c1.3,1,2.5,2,4.4,2.6c-0.1-0.3-0.3-0.7-0.4-1c-1.3-0.6-2.4-1.4-3.4-2.2
|
||||
c-1.9-1.5-3.9-3.1-7.9-3.1c-4,0-5.9,1.6-7.9,3.1c-1.9,1.5-3.7,2.9-7.4,2.9c-3.7,0-5.5-1.4-7.4-2.9c-1.9-1.5-3.9-3.1-7.9-3.1
|
||||
c-4,0-5.9,1.6-7.9,3.1c-1.9,1.5-3.7,2.9-7.4,2.9c-3.7,0-5.5-1.4-7.4-2.9c-1.9-1.5-3.9-3.1-7.9-3.1c-4,0-5.9,1.6-7.9,3.1
|
||||
c-1.9,1.5-3.7,2.9-7.4,2.9c-3.7,0-5.5-1.4-7.4-2.9c-0.5-0.4-0.9-0.7-1.4-1.1c-0.1,0.2-0.3,0.5-0.4,0.8c0.4,0.3,0.9,0.6,1.3,1
|
||||
C114.1,61.2,116.1,62.8,120.1,62.8z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st3" d="M81.7,161.9l-18.2,41.2h-5.1l-18.2-41.2h5.1L61,197.4l15.6-35.5H81.7z"/>
|
||||
<path class="st3" d="M91.7,166.3v13.6h22.4v4.4H91.7v14.3h24.8v4.4H87v-41.2h29.6v4.4H91.7z"/>
|
||||
<path class="st3" d="M150.9,198.7v4.4h-27.2v-41.2h4.7v36.8H150.9z"/>
|
||||
<path class="st3" d="M160.9,166.3v13.6h22.4v4.4h-22.4v14.3h24.8v4.4h-29.6v-41.2h29.6v4.4H160.9z"/>
|
||||
<path class="st3" d="M223.1,181.3c-1.1,1.9-2.6,3.4-4.6,4.6c-2,1.2-4.2,1.9-6.6,2.2l10.5,14.9h-5.3l-10.4-14.8h-9.1v14.8h-4.7
|
||||
v-41.2h16.7c2.8,0,5.4,0.6,7.7,1.7c2.3,1.1,4.1,2.7,5.5,4.7c1.3,2,2,4.3,2,6.8C224.8,177.4,224.2,179.5,223.1,181.3z M197.5,183.9
|
||||
h11.2c3.4,0,6.1-0.8,8.2-2.3c2.1-1.6,3.1-3.7,3.1-6.4c0-2.7-1-4.9-3.1-6.4c-2.1-1.6-4.8-2.3-8.2-2.3h-11.2V183.9z"/>
|
||||
<path class="st3" d="M239.6,200.9c-3.3-1.9-5.8-4.5-7.8-7.8c-1.9-3.3-2.9-6.8-2.9-10.6c0-3.8,1-7.3,2.9-10.6
|
||||
c1.9-3.3,4.5-5.9,7.8-7.8c3.3-1.9,6.8-2.9,10.5-2.9c3.8,0,7.2,1,10.5,2.9c3.2,1.9,5.8,4.5,7.7,7.8c1.9,3.3,2.9,6.8,2.9,10.6
|
||||
c0,3.8-1,7.3-2.9,10.6c-1.9,3.3-4.5,5.9-7.7,7.8c-3.2,1.9-6.7,2.9-10.5,2.9C246.3,203.8,242.8,202.9,239.6,200.9z M258.2,197.2
|
||||
c2.5-1.6,4.5-3.6,6-6.2c1.5-2.6,2.2-5.4,2.2-8.5c0-3-0.7-5.8-2.2-8.4c-1.5-2.6-3.5-4.7-6-6.2c-2.5-1.5-5.2-2.3-8.1-2.3
|
||||
c-2.9,0-5.6,0.8-8.1,2.3c-2.5,1.5-4.5,3.6-6,6.2c-1.5,2.6-2.2,5.4-2.2,8.4c0,3,0.8,5.9,2.2,8.5c1.5,2.6,3.5,4.7,6,6.2
|
||||
s5.2,2.3,8.1,2.3C253,199.5,255.7,198.7,258.2,197.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 34 KiB |
@@ -1,103 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 311.5 245.2" style="enable-background:new 0 0 311.5 245.2;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:none;}
|
||||
.st2{fill:#009BDB;}
|
||||
.st3{fill:#717074;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M211.5,83.4c0-0.7,0.1-1.4,0.1-2c0-0.7,0-1.4-0.1-2V83.4z"/>
|
||||
<path class="st2" d="M196.3,115.5c-3.3,0-4.9,1.2-6.7,2.7c-2,1.6-4.2,3.4-8.6,3.4c-4.3,0-6.6-1.8-8.6-3.4
|
||||
c-1.8-1.4-3.4-2.7-6.7-2.7c-3.3,0-4.9,1.2-6.7,2.7c-2,1.6-4.2,3.4-8.6,3.4c-4.3,0-6.6-1.8-8.6-3.4c-1.8-1.4-3.4-2.7-6.7-2.7
|
||||
s-4.9,1.2-6.7,2.7c-1.3,1-2.7,2.2-4.8,2.8c8.3,7.3,18.9,12,30.5,13c0.3,0,0.6,0.1,1,0.1c1.1,0.1,2.3,0.1,3.4,0.1
|
||||
c1.2,0,2.3-0.1,3.4-0.1c0.3,0,0.6,0,1-0.1c14.2-1.2,26.8-8,35.6-18.2C198,115.7,197.2,115.5,196.3,115.5z"/>
|
||||
<path class="st2" d="M120.1,76.1c3.6,0,5.3-1.4,7.2-2.9c2-1.6,4-3.2,8-3.2c4,0,6.1,1.6,8,3.2c1.9,1.5,3.6,2.9,7.2,2.9
|
||||
c3.6,0,5.3-1.4,7.2-2.9c2-1.6,4-3.2,8-3.2c4,0,6.1,1.6,8,3.2c1.9,1.5,3.6,2.9,7.2,2.9c3.6,0,5.3-1.4,7.2-2.9c2-1.6,4-3.2,8-3.2
|
||||
c4,0,6.1,1.6,8,3.2c1.8,1.5,3.6,2.8,7,2.9c-0.5-4.8-1.6-9.5-3.3-13.8c-1.8-0.6-3.1-1.6-4.4-2.6c-1.9-1.5-3.7-2.9-7.4-2.9
|
||||
c-3.7,0-5.5,1.4-7.4,2.9c-1.9,1.5-3.9,3.1-7.9,3.1c-4,0-5.9-1.6-7.9-3.1c-1.9-1.5-3.7-2.9-7.4-2.9c-3.7,0-5.5,1.4-7.4,2.9
|
||||
c-1.9,1.5-3.9,3.1-7.9,3.1c-4,0-5.9-1.6-7.9-3.1c-1.9-1.5-3.7-2.9-7.4-2.9s-5.5,1.4-7.4,2.9c-1.9,1.5-3.9,3.1-7.9,3.1
|
||||
c-4,0-5.9-1.6-7.9-3.1c-0.4-0.3-0.9-0.7-1.3-1c-1.7,3.6-3.1,7.5-3.9,11.6c2.7,0.5,4.3,1.7,5.9,3C114.8,74.7,116.5,76.1,120.1,76.1
|
||||
z"/>
|
||||
<path class="st2" d="M120.1,61.9c3.7,0,5.5-1.4,7.4-2.9c1.9-1.5,3.9-3.1,7.9-3.1c4,0,5.9,1.6,7.9,3.1c1.9,1.5,3.7,2.9,7.4,2.9
|
||||
c3.7,0,5.5-1.4,7.4-2.9c1.9-1.5,3.9-3.1,7.9-3.1c4,0,5.9,1.6,7.9,3.1c1.9,1.5,3.7,2.9,7.4,2.9c3.7,0,5.5-1.4,7.4-2.9
|
||||
c1.9-1.5,3.9-3.1,7.9-3.1c4,0,5.9,1.6,7.9,3.1c1.1,0.8,2.1,1.7,3.4,2.2c-7.9-19.2-26.9-32.8-48.9-32.8c-20.8,0-38.7,12-47.4,29.5
|
||||
c0.5,0.4,1,0.7,1.4,1.1C114.6,60.5,116.4,61.9,120.1,61.9z"/>
|
||||
<path class="st2" d="M196.3,100.8c-3.4,0-5,1.3-6.8,2.8c-1.9,1.5-4.1,3.3-8.4,3.3c-4.2,0-6.4-1.7-8.4-3.3
|
||||
c-1.9-1.5-3.5-2.8-6.8-2.8c-3.4,0-5,1.3-6.8,2.8c-1.9,1.5-4.1,3.3-8.4,3.3c-4.2,0-6.4-1.7-8.4-3.3c-1.9-1.5-3.5-2.8-6.8-2.8
|
||||
c-3.4,0-5,1.3-6.8,2.8c-1.9,1.5-4.1,3.3-8.4,3.3c-4.2,0-6.4-1.7-8.4-3.3c-0.5-0.4-1-0.8-1.5-1.1c2.6,6,6.3,11.4,10.9,16
|
||||
c2.6-0.2,4-1.4,5.6-2.6c2-1.6,4.2-3.4,8.6-3.4c4.3,0,6.6,1.8,8.6,3.4c1.8,1.4,3.4,2.7,6.7,2.7c3.3,0,4.9-1.2,6.7-2.7
|
||||
c2-1.6,4.2-3.4,8.6-3.4c4.3,0,6.6,1.8,8.6,3.4c1.8,1.4,3.4,2.7,6.7,2.7c3.3,0,4.9-1.2,6.7-2.7c2-1.6,4.2-3.4,8.6-3.4
|
||||
c1.8,0,3.3,0.3,4.5,0.8c1.9-2.5,3.5-5.1,5-7.9c-1-0.6-1.8-1.2-2.6-1.8C201.3,102.1,199.6,100.8,196.3,100.8z"/>
|
||||
<path class="st2" d="M211.4,77.5c-4,0-6-1.6-8-3.2c-1.9-1.5-3.6-2.9-7.2-2.9c-3.6,0-5.3,1.4-7.2,2.9c-2,1.6-4,3.2-8,3.2
|
||||
c-4,0-6.1-1.6-8-3.2c-1.9-1.5-3.6-2.9-7.2-2.9c-3.6,0-5.3,1.4-7.2,2.9c-2,1.6-4,3.2-8,3.2c-4,0-6.1-1.6-8-3.2
|
||||
c-1.9-1.5-3.6-2.9-7.2-2.9c-3.6,0-5.3,1.4-7.2,2.9c-2,1.6-4,3.2-8,3.2c-4,0-6.1-1.6-8-3.2c-1.5-1.2-3-2.3-5.3-2.7
|
||||
c-0.5,2.5-0.8,5.1-0.9,7.7c0,0.7-0.1,1.4-0.1,2c0,0.7,0,1.4,0.1,2c0,0.3,0,0.6,0,0.9c3.5,0.3,5.4,1.8,7.2,3.2
|
||||
c1.8,1.4,3.5,2.8,7,2.8c3.5,0,5.2-1.4,7-2.8c1.9-1.5,4.1-3.2,8.2-3.2s6.3,1.7,8.2,3.2c1.8,1.4,3.5,2.8,7,2.8c3.5,0,5.2-1.4,7-2.8
|
||||
c1.9-1.5,4.1-3.2,8.2-3.2c4.1,0,6.3,1.7,8.2,3.2c1.8,1.4,3.5,2.8,7,2.8c3.5,0,5.2-1.4,7-2.8c1.9-1.5,4.1-3.2,8.2-3.2
|
||||
c4.1,0,6.3,1.7,8.2,3.2c1.7,1.3,3.3,2.6,6.3,2.8c0.3-1.6,0.5-3.2,0.6-4.9c0-0.6,0.1-1.3,0.1-1.9v-4.1
|
||||
C211.5,78.6,211.4,77.5,211.4,77.5z"/>
|
||||
<path class="st2" d="M196.3,86.1c-3.5,0-5.2,1.4-7,2.8c-1.9,1.5-4.1,3.2-8.2,3.2c-4.1,0-6.3-1.7-8.2-3.2c-1.8-1.4-3.5-2.8-7-2.8
|
||||
c-3.5,0-5.2,1.4-7,2.8c-1.9,1.5-4.1,3.2-8.2,3.2c-4.1,0-6.3-1.7-8.2-3.2c-1.8-1.4-3.5-2.8-7-2.8c-3.5,0-5.2,1.4-7,2.8
|
||||
c-1.9,1.5-4.1,3.2-8.2,3.2c-4.1,0-6.3-1.7-8.2-3.2c-1.6-1.3-3.1-2.5-5.8-2.8c0.4,4.5,1.4,8.7,2.8,12.8c1.9,0.6,3.2,1.7,4.4,2.7
|
||||
c1.9,1.5,3.5,2.8,6.8,2.8c3.4,0,5-1.3,6.8-2.8c1.9-1.5,4.1-3.3,8.4-3.3c4.2,0,6.4,1.7,8.4,3.3c1.9,1.5,3.5,2.8,6.8,2.8
|
||||
c3.4,0,5-1.3,6.8-2.8c1.9-1.5,4.1-3.3,8.4-3.3c4.2,0,6.4,1.7,8.4,3.3c1.9,1.5,3.5,2.8,6.8,2.8c3.4,0,5-1.3,6.8-2.8
|
||||
c1.9-1.5,4.1-3.3,8.4-3.3c4.2,0,6.4,1.7,8.4,3.3c0.7,0.6,1.4,1.1,2.2,1.6c1.6-3.5,2.8-7.2,3.6-11.1c-3.5-0.3-5.4-1.8-7.2-3.2
|
||||
C201.5,87.5,199.7,86.1,196.3,86.1z"/>
|
||||
<path class="st0" d="M187.7,115.9c-1.8,1.4-3.4,2.7-6.7,2.7c-3.3,0-4.9-1.2-6.7-2.7c-2-1.6-4.2-3.4-8.6-3.4
|
||||
c-4.3,0-6.6,1.8-8.6,3.4c-1.8,1.4-3.4,2.7-6.7,2.7c-3.3,0-4.9-1.2-6.7-2.7c-2-1.6-4.2-3.4-8.6-3.4c-4.3,0-6.6,1.8-8.6,3.4
|
||||
c-1.6,1.3-3,2.4-5.6,2.6c0.9,0.9,1.8,1.7,2.7,2.5c2.1-0.6,3.5-1.8,4.8-2.8c1.8-1.4,3.4-2.7,6.7-2.7c3.3,0,4.9,1.2,6.7,2.7
|
||||
c2,1.6,4.2,3.4,8.6,3.4c4.3,0,6.6-1.8,8.6-3.4c1.8-1.4,3.4-2.7,6.7-2.7c3.3,0,4.9,1.2,6.7,2.7c2,1.6,4.2,3.4,8.6,3.4
|
||||
c4.3,0,6.6-1.8,8.6-3.4c1.8-1.4,3.4-2.7,6.7-2.7c0.9,0,1.7,0.1,2.4,0.3c0.7-0.8,1.4-1.7,2-2.5c-1.2-0.5-2.7-0.8-4.5-0.8
|
||||
C191.9,112.5,189.7,114.3,187.7,115.9z"/>
|
||||
<path class="st0" d="M196.3,98.4c-4.2,0-6.4,1.7-8.4,3.3c-1.9,1.5-3.5,2.8-6.8,2.8c-3.4,0-5-1.3-6.8-2.8c-1.9-1.5-4.1-3.3-8.4-3.3
|
||||
c-4.2,0-6.4,1.7-8.4,3.3c-1.9,1.5-3.5,2.8-6.8,2.8c-3.4,0-5-1.3-6.8-2.8c-1.9-1.5-4.1-3.3-8.4-3.3c-4.2,0-6.4,1.7-8.4,3.3
|
||||
c-1.9,1.5-3.5,2.8-6.8,2.8c-3.4,0-5-1.3-6.8-2.8c-1.2-1-2.5-2-4.4-2.7c0.4,1.2,0.9,2.3,1.4,3.5c0.5,0.3,1,0.7,1.5,1.1
|
||||
c1.9,1.5,4.1,3.3,8.4,3.3c4.2,0,6.4-1.7,8.4-3.3c1.9-1.5,3.5-2.8,6.8-2.8c3.4,0,5,1.3,6.8,2.8c1.9,1.5,4.1,3.3,8.4,3.3
|
||||
c4.2,0,6.4-1.7,8.4-3.3c1.9-1.5,3.5-2.8,6.8-2.8c3.4,0,5,1.3,6.8,2.8c1.9,1.5,4.1,3.3,8.4,3.3c4.2,0,6.4-1.7,8.4-3.3
|
||||
c1.9-1.5,3.5-2.8,6.8-2.8c3.4,0,5,1.3,6.8,2.8c0.8,0.6,1.6,1.3,2.6,1.8c0.4-0.7,0.7-1.5,1.1-2.2c-0.8-0.4-1.4-1-2.2-1.6
|
||||
C202.7,100.1,200.5,98.4,196.3,98.4z"/>
|
||||
<path class="st0" d="M196.3,84.2c-4.1,0-6.3,1.7-8.2,3.2c-1.8,1.4-3.5,2.8-7,2.8c-3.5,0-5.2-1.4-7-2.8c-1.9-1.5-4.1-3.2-8.2-3.2
|
||||
c-4.1,0-6.3,1.7-8.2,3.2c-1.8,1.4-3.5,2.8-7,2.8c-3.5,0-5.2-1.4-7-2.8c-1.9-1.5-4.1-3.2-8.2-3.2c-4.1,0-6.3,1.7-8.2,3.2
|
||||
c-1.8,1.4-3.5,2.8-7,2.8c-3.5,0-5.2-1.4-7-2.8c-1.7-1.4-3.7-2.9-7.2-3.2c0,0.6,0.1,1.3,0.1,1.9c2.7,0.3,4.2,1.5,5.8,2.8
|
||||
c1.9,1.5,4.1,3.2,8.2,3.2c4.1,0,6.3-1.7,8.2-3.2c1.8-1.4,3.5-2.8,7-2.8c3.5,0,5.2,1.4,7,2.8c1.9,1.5,4.1,3.2,8.2,3.2
|
||||
c4.1,0,6.3-1.7,8.2-3.2c1.8-1.4,3.5-2.8,7-2.8c3.5,0,5.2,1.4,7,2.8c1.9,1.5,4.1,3.2,8.2,3.2c4.1,0,6.3-1.7,8.2-3.2
|
||||
c1.8-1.4,3.5-2.8,7-2.8c3.5,0,5.2,1.4,7,2.8c1.7,1.4,3.7,2.9,7.2,3.2c0.1-0.6,0.2-1.3,0.3-1.9c-3-0.2-4.6-1.4-6.3-2.8
|
||||
C202.6,85.9,200.4,84.2,196.3,84.2z"/>
|
||||
<path class="st0" d="M120.1,77.5c4,0,6.1-1.6,8-3.2c1.9-1.5,3.6-2.9,7.2-2.9c3.6,0,5.3,1.4,7.2,2.9c2,1.6,4,3.2,8,3.2
|
||||
c4,0,6.1-1.6,8-3.2c1.9-1.5,3.6-2.9,7.2-2.9c3.6,0,5.3,1.4,7.2,2.9c2,1.6,4,3.2,8,3.2c4,0,6.1-1.6,8-3.2c1.9-1.5,3.6-2.9,7.2-2.9
|
||||
c3.6,0,5.3,1.4,7.2,2.9c1.9,1.5,4,3.1,8,3.2l0-0.1c0-0.4-0.1-0.8-0.1-1.3c-3.4-0.1-5.2-1.4-7-2.9c-2-1.6-4-3.2-8-3.2
|
||||
c-4,0-6.1,1.6-8,3.2c-1.9,1.5-3.6,2.9-7.2,2.9c-3.6,0-5.3-1.4-7.2-2.9c-2-1.6-4-3.2-8-3.2c-4,0-6.1,1.6-8,3.2
|
||||
c-1.9,1.5-3.6,2.9-7.2,2.9c-3.6,0-5.3-1.4-7.2-2.9c-2-1.6-4-3.2-8-3.2c-4,0-6.1,1.6-8,3.2c-1.9,1.5-3.6,2.9-7.2,2.9
|
||||
c-3.6,0-5.3-1.4-7.2-2.9c-1.6-1.3-3.2-2.5-5.9-3c-0.1,0.4-0.2,0.9-0.3,1.3c2.4,0.4,3.8,1.5,5.3,2.7C114,75.9,116,77.5,120.1,77.5z
|
||||
"/>
|
||||
<path class="st0" d="M120.1,62.8c4,0,5.9-1.6,7.9-3.1c1.9-1.5,3.7-2.9,7.4-2.9s5.5,1.4,7.4,2.9c1.9,1.5,3.9,3.1,7.9,3.1
|
||||
c4,0,5.9-1.6,7.9-3.1c1.9-1.5,3.7-2.9,7.4-2.9c3.7,0,5.5,1.4,7.4,2.9c1.9,1.5,3.9,3.1,7.9,3.1c4,0,5.9-1.6,7.9-3.1
|
||||
c1.9-1.5,3.7-2.9,7.4-2.9c3.7,0,5.5,1.4,7.4,2.9c1.3,1,2.5,2,4.4,2.6c-0.1-0.3-0.3-0.7-0.4-1c-1.3-0.6-2.4-1.4-3.4-2.2
|
||||
c-1.9-1.5-3.9-3.1-7.9-3.1c-4,0-5.9,1.6-7.9,3.1c-1.9,1.5-3.7,2.9-7.4,2.9c-3.7,0-5.5-1.4-7.4-2.9c-1.9-1.5-3.9-3.1-7.9-3.1
|
||||
c-4,0-5.9,1.6-7.9,3.1c-1.9,1.5-3.7,2.9-7.4,2.9c-3.7,0-5.5-1.4-7.4-2.9c-1.9-1.5-3.9-3.1-7.9-3.1c-4,0-5.9,1.6-7.9,3.1
|
||||
c-1.9,1.5-3.7,2.9-7.4,2.9c-3.7,0-5.5-1.4-7.4-2.9c-0.5-0.4-0.9-0.7-1.4-1.1c-0.1,0.2-0.3,0.5-0.4,0.8c0.4,0.3,0.9,0.6,1.3,1
|
||||
C114.1,61.2,116.1,62.8,120.1,62.8z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M81.7,161.9l-18.2,41.2h-5.1l-18.2-41.2h5.1L61,197.4l15.6-35.5H81.7z"/>
|
||||
<path class="st0" d="M91.7,166.3v13.6h22.4v4.4H91.7v14.3h24.8v4.4H87v-41.2h29.6v4.4H91.7z"/>
|
||||
<path class="st0" d="M150.9,198.7v4.4h-27.2v-41.2h4.7v36.8H150.9z"/>
|
||||
<path class="st0" d="M160.9,166.3v13.6h22.4v4.4h-22.4v14.3h24.8v4.4h-29.6v-41.2h29.6v4.4H160.9z"/>
|
||||
<path class="st0" d="M223.1,181.3c-1.1,1.9-2.6,3.4-4.6,4.6c-2,1.2-4.2,1.9-6.6,2.2l10.5,14.9h-5.3l-10.4-14.8h-9.1v14.8h-4.7
|
||||
v-41.2h16.7c2.8,0,5.4,0.6,7.7,1.7c2.3,1.1,4.1,2.7,5.5,4.7c1.3,2,2,4.3,2,6.8C224.8,177.4,224.2,179.5,223.1,181.3z M197.5,183.9
|
||||
h11.2c3.4,0,6.1-0.8,8.2-2.3c2.1-1.6,3.1-3.7,3.1-6.4c0-2.7-1-4.9-3.1-6.4c-2.1-1.6-4.8-2.3-8.2-2.3h-11.2V183.9z"/>
|
||||
<path class="st0" d="M239.6,200.9c-3.3-1.9-5.8-4.5-7.8-7.8c-1.9-3.3-2.9-6.8-2.9-10.6c0-3.8,1-7.3,2.9-10.6
|
||||
c1.9-3.3,4.5-5.9,7.8-7.8c3.3-1.9,6.8-2.9,10.5-2.9c3.8,0,7.2,1,10.5,2.9c3.2,1.9,5.8,4.5,7.7,7.8c1.9,3.3,2.9,6.8,2.9,10.6
|
||||
c0,3.8-1,7.3-2.9,10.6c-1.9,3.3-4.5,5.9-7.7,7.8c-3.2,1.9-6.7,2.9-10.5,2.9C246.3,203.8,242.8,202.9,239.6,200.9z M258.2,197.2
|
||||
c2.5-1.6,4.5-3.6,6-6.2c1.5-2.6,2.2-5.4,2.2-8.5c0-3-0.7-5.8-2.2-8.4c-1.5-2.6-3.5-4.7-6-6.2c-2.5-1.5-5.2-2.3-8.1-2.3
|
||||
c-2.9,0-5.6,0.8-8.1,2.3s-4.5,3.6-6,6.2s-2.2,5.4-2.2,8.4c0,3,0.8,5.9,2.2,8.5c1.5,2.6,3.5,4.7,6,6.2c2.5,1.6,5.2,2.3,8.1,2.3
|
||||
C253,199.5,255.7,198.7,258.2,197.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.3 KiB |
@@ -1,3 +1,80 @@
|
||||
## v1.6.3
|
||||
### 2021-07-30
|
||||
|
||||
### Download
|
||||
https://github.com/vmware-tanzu/velero/releases/tag/v1.6.3
|
||||
|
||||
### Container Image
|
||||
`velero/velero:v1.6.3`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.6/
|
||||
|
||||
### Upgrading
|
||||
https://velero.io/docs/v1.6/upgrade-to-1.6/
|
||||
|
||||
### Highlights
|
||||
|
||||
This release introduces changes to provide compatibility with Kubernetes v1.22.
|
||||
|
||||
The `apiextensions.k8s.io/v1beta1` API version of `CustomResourceDefinition` will no longer be served in Kubernetes v1.22.
|
||||
Velero will now use the cluster preferred API version for the `CustomResourceDefinition`s that it creates.
|
||||
|
||||
If you are using Kubernetes v1.15 or earlier, the `apiextensions.k8s.io/v1beta1` API version will be used.
|
||||
If you are using Kubernetes v1.22 or later, the `apiextensions.k8s.io/v1` API version will be used.
|
||||
For clusters between these versions, the cluster preferred API version will be used.
|
||||
|
||||
The `rbac.authorization.k8s.io/v1beta1` API version of `ClusterRoleBinding` will no longer be served in Kubernetes v1.22.
|
||||
Velero will now use the `rbac.authorization.k8s.io/v1` API version for the `ClusterRoleBinding`s that it creates.
|
||||
This API version was introduced in Kubernetes v1.8.
|
||||
|
||||
### All Changes
|
||||
|
||||
* enable e2e tests to choose crd apiVersion (#3941, @sseago)
|
||||
* Upgrade Velero ClusterRoleBinding to use v1 API (#3995, @jenting)
|
||||
* Install Kubernetes preferred CRDs API version (v1beta1/v1). (#3999, @jenting)
|
||||
* Use the cluster preferred CRD API version when polling for Velero CRD readiness. (#4015, @zubron)
|
||||
* Add a RestoreItemAction plugin (`velero.io/apiservice`) which skips the restore of any `APIService` which is managed by Kubernetes. These are identified using the `kube-aggregator.kubernetes.io/automanaged` label. (#4028, @zubron)
|
||||
|
||||
## v1.6.2
|
||||
### 2021-07-16
|
||||
|
||||
### Download
|
||||
https://github.com/vmware-tanzu/velero/releases/tag/v1.6.2
|
||||
|
||||
### Container Image
|
||||
`velero/velero:v1.6.2`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.6/
|
||||
|
||||
### Upgrading
|
||||
https://velero.io/docs/v1.6/upgrade-to-1.6/
|
||||
|
||||
This release contains no user facing changes but includes fixes for CVE-2021-3121 and CVE-2021-3580.
|
||||
|
||||
## v1.6.1
|
||||
### 2021-06-21
|
||||
|
||||
### Download
|
||||
https://github.com/vmware-tanzu/velero/releases/tag/v1.6.1
|
||||
|
||||
### Container Image
|
||||
`velero/velero:v1.6.1`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.6/
|
||||
|
||||
### Upgrading
|
||||
https://velero.io/docs/v1.6/upgrade-to-1.6/
|
||||
|
||||
### All Changes
|
||||
|
||||
* Fix CR restore regression introduced in 1.6 restore progress. (#3845, @sseago)
|
||||
* Skip the restore of volumes that originally came from a projected volume when using restic. (#3877, @zubron)
|
||||
* skip backuping projected volume when using restic (#3866, @alaypatel07)
|
||||
* 🐛 Fix plugin name derivation from image name (#3711, @ashish-amarnath)
|
||||
|
||||
## v1.6.0
|
||||
### 2021-04-12
|
||||
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
## v1.7.0
|
||||
### 2021-09-07
|
||||
|
||||
### Download
|
||||
https://github.com/vmware-tanzu/velero/releases/tag/v1.7.0
|
||||
|
||||
### Container Image
|
||||
`velero/velero:v1.7.0`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.7/
|
||||
|
||||
### Upgrading
|
||||
https://velero.io/docs/v1.7/upgrade-to-1.7/
|
||||
|
||||
### Highlights
|
||||
|
||||
#### Distroless images
|
||||
|
||||
The Velero container images now use [distroless base images](https://github.com/GoogleContainerTools/distroless).
|
||||
Using distroless images as the base ensures that only the packages and programs necessary for running Velero are included.
|
||||
Unrelated libraries and OS packages, that often contain security vulnerabilities, are now excluded.
|
||||
This change reduces the size of both the server and restic restore helper image by approximately 62MB.
|
||||
|
||||
As the [distroless](https://github.com/GoogleContainerTools/distroless) images do not contain a shell, it will no longer be possible to exec into Velero containers using these images.
|
||||
|
||||
#### New "debug" command
|
||||
|
||||
This release introduces the new `velero debug` command.
|
||||
This command collects information about a Velero installation, such as pod logs and resources managed by Velero, in a tarball which can be provided to the Velero maintainer team to help diagnose issues.
|
||||
|
||||
### All changes
|
||||
|
||||
* Distinguish between different unnamed node ports when preserving (#4026, @sseago)
|
||||
* Validate namespace in Velero backup create command (#4057, @codegold79)
|
||||
* Empty the "ClusterIPs" along with "ClusterIP" when "ClusterIP" isn't "None" (#4101, @ywk253100)
|
||||
* Add a RestoreItemAction plugin (`velero.io/apiservice`) which skips the restore of any `APIService` which is managed by Kubernetes. These are identified using the `kube-aggregator.kubernetes.io/automanaged` label. (#4028, @zubron)
|
||||
* Change the base image to distroless (#4055, @ywk253100)
|
||||
* Updated the version of velero/velero-plugin-for-aws version from v1.2.0 to v1.2.1 (#4064, @kahirokunn)
|
||||
* Skip the backup and restore of DownwardAPI volumes when using restic. (#4076, @zubron)
|
||||
* Bump up Go to 1.16 (#3990, @reasonerjt)
|
||||
* Fix restic error when volume is emptyDir and Pod not running (#3993, @mahaupt)
|
||||
* Select the velero deployment with both label and container name (#3996, @ywk253100)
|
||||
* Wait for the namespace to be deleted before removing the CRDs during uninstall. This deprecates the `--wait` flag of the `uninstall` command (#4007, @ywk253100)
|
||||
* Use the cluster preferred CRD API version when polling for Velero CRD readiness. (#4015, @zubron)
|
||||
* Implement velero debug (#4022, @reasonerjt)
|
||||
* Skip the restore of volumes that originally came from a projected volume when using restic. (#3877, @zubron)
|
||||
* Run the E2E test with kind(provision various versions of k8s cluster) and MinIO on Github Action (#3912, @ywk253100)
|
||||
* Fix -install-velero flag for e2e tests (#3919, @jaidevmane)
|
||||
* Upgrade Velero ClusterRoleBinding to use v1 API (#3926, @jenting)
|
||||
* enable e2e tests to choose crd apiVersion (#3941, @sseago)
|
||||
* Fixing multipleNamespaceTest bug - Missing expect statement in test (#3983, @jaidevmane)
|
||||
* Add --client-page-size flag to server to allow chunking Kubernetes API LIST calls across multiple requests on large clusters (#3823, @dharmab)
|
||||
* Fix CR restore regression introduced in 1.6 restore progress. (#3845, @sseago)
|
||||
* Use region specified in the BackupStorageLocation spec when getting restic repo identifier. Originally fixed by @jala-dx in #3617. (#3857, @zubron)
|
||||
* skip backuping projected volume when using restic (#3866, @alaypatel07)
|
||||
* Install Kubernetes preferred CRDs API version (v1beta1/v1). (#3614, @jenting)
|
||||
* Add Label to BackupSpec so that labels can explicitly be provided to Schedule.Spec.Template.Metadata.Labels which will be reflected on the backups created. (#3641, @arush-sal)
|
||||
* Add PVC UID label to PodVolumeRestore (#3792, @sseago)
|
||||
* Support pulling plugin images by digest (#3803, @2uasimojo)
|
||||
* Added BackupPhaseUploading and BackupPhaseUploadingPartialFailure backup phases as part of Upload Progress Monitoring. (#3805, @dsmithuchida)
|
||||
|
||||
Uploading (new)
|
||||
The "Uploading" phase signifies that the main part of the backup, including
|
||||
snapshotting has completed successfully and uploading is continuing. In
|
||||
the event of an error during uploading, the phase will change to
|
||||
UploadingPartialFailure. On success, the phase changes to Completed. The
|
||||
backup cannot be restored from when it is in the Uploading state.
|
||||
|
||||
UploadingPartialFailure (new)
|
||||
The "UploadingPartialFailure" phase signifies that the main part of the backup,
|
||||
including snapshotting has completed, but there were partial failures either
|
||||
during the main part or during the uploading. The backup cannot be restored
|
||||
from when it is in the UploadingPartialFailure state.
|
||||
* 🐛 Fix plugin name derivation from image name (#3711, @ashish-amarnath)
|
||||
* ✨ ⚠️ Remove CSI volumesnapshot artifact deletion
|
||||
|
||||
This change requires https://github.com/vmware-tanzu/velero-plugin-for-csi/pull/86 for Velero to continue
|
||||
deleting of CSI volumesnapshots when the corresponding backups are deleted. (#3734, @ashish-amarnath)
|
||||
* use unstructured to marshal selective fields for service restore action (#3789, @alaypatel07)
|
||||
@@ -1 +0,0 @@
|
||||
Add upgrade test in E2E test
|
||||
@@ -1 +0,0 @@
|
||||
Verify group before treating resource as cohabitating
|
||||
@@ -1 +0,0 @@
|
||||
Fix plugins incompatible issue in upgrade test
|
||||
@@ -1 +0,0 @@
|
||||
Refine tag-release.sh to align with change in release process
|
||||
@@ -1 +0,0 @@
|
||||
Fix CVE-2020-29652 and CVE-2020-26160
|
||||
@@ -1 +0,0 @@
|
||||
Don't create a backup immediately after creating a schedule
|
||||
@@ -307,13 +307,6 @@ spec:
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
metadata:
|
||||
properties:
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
orderedResources:
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
||||
@@ -205,10 +205,8 @@ spec:
|
||||
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)".
|
||||
unchanged. The $(VAR_NAME) syntax can
|
||||
be escaped with a double $$, ie: $$(VAR_NAME).
|
||||
Escaped references will never be expanded,
|
||||
regardless of whether the variable exists
|
||||
or not. Cannot be updated. More info:
|
||||
@@ -223,14 +221,12 @@ spec:
|
||||
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'
|
||||
input string will be unchanged. The $(VAR_NAME)
|
||||
syntax can be escaped with a double $$,
|
||||
ie: $$(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
|
||||
@@ -248,19 +244,17 @@ spec:
|
||||
value:
|
||||
description: 'Variable references
|
||||
$(VAR_NAME) are expanded using the
|
||||
previously defined environment variables
|
||||
previous 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 "".'
|
||||
The $(VAR_NAME) syntax can be escaped
|
||||
with a double $$, ie: $$(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
|
||||
@@ -810,30 +804,6 @@ spec:
|
||||
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
|
||||
@@ -1036,30 +1006,6 @@ spec:
|
||||
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
|
||||
@@ -1071,7 +1017,7 @@ spec:
|
||||
resources:
|
||||
description: 'Compute Resources required
|
||||
by this container. Cannot be updated.
|
||||
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
|
||||
More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
|
||||
properties:
|
||||
limits:
|
||||
additionalProperties:
|
||||
@@ -1082,7 +1028,7 @@ spec:
|
||||
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/'
|
||||
More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
|
||||
type: object
|
||||
requests:
|
||||
additionalProperties:
|
||||
@@ -1097,14 +1043,12 @@ spec:
|
||||
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/'
|
||||
More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
|
||||
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.
|
||||
description: 'Security options the pod should
|
||||
run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/
|
||||
More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/'
|
||||
properties:
|
||||
allowPrivilegeEscalation:
|
||||
@@ -1273,25 +1217,6 @@ spec:
|
||||
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
|
||||
@@ -1317,7 +1242,9 @@ spec:
|
||||
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'
|
||||
updated. This is a beta feature enabled
|
||||
by the StartupProbe feature flag. More
|
||||
info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
|
||||
properties:
|
||||
exec:
|
||||
description: One and only one of the
|
||||
@@ -1444,30 +1371,6 @@ spec:
|
||||
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
|
||||
|
||||
@@ -320,13 +320,6 @@ spec:
|
||||
"value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
metadata:
|
||||
properties:
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
orderedResources:
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
||||
@@ -303,13 +303,6 @@ spec:
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
metadata:
|
||||
properties:
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
orderedResources:
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
||||
@@ -202,14 +202,12 @@ spec:
|
||||
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'
|
||||
in the input string will be unchanged. The
|
||||
$(VAR_NAME) syntax can be escaped with a
|
||||
double $$, ie: $$(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
|
||||
@@ -220,14 +218,12 @@ spec:
|
||||
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'
|
||||
input string will be unchanged. The $(VAR_NAME)
|
||||
syntax can be escaped with a double $$,
|
||||
ie: $$(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
|
||||
@@ -244,19 +240,17 @@ spec:
|
||||
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 "".'
|
||||
are expanded using the previous 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. The $(VAR_NAME)
|
||||
syntax can be escaped with a double
|
||||
$$, ie: $$(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
|
||||
@@ -798,29 +792,6 @@ spec:
|
||||
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
|
||||
@@ -1020,29 +991,6 @@ spec:
|
||||
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
|
||||
@@ -1054,7 +1002,7 @@ spec:
|
||||
resources:
|
||||
description: 'Compute Resources required by
|
||||
this container. Cannot be updated. More
|
||||
info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
|
||||
info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
|
||||
properties:
|
||||
limits:
|
||||
additionalProperties:
|
||||
@@ -1065,7 +1013,7 @@ spec:
|
||||
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/'
|
||||
More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
|
||||
type: object
|
||||
requests:
|
||||
additionalProperties:
|
||||
@@ -1079,14 +1027,12 @@ spec:
|
||||
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/'
|
||||
value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
|
||||
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.
|
||||
description: 'Security options the pod should
|
||||
run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/
|
||||
More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/'
|
||||
properties:
|
||||
allowPrivilegeEscalation:
|
||||
@@ -1251,24 +1197,6 @@ spec:
|
||||
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
|
||||
@@ -1292,8 +1220,9 @@ spec:
|
||||
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'
|
||||
operation. This cannot be updated. This
|
||||
is a beta feature enabled by the StartupProbe
|
||||
feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
|
||||
properties:
|
||||
exec:
|
||||
description: One and only one of the following
|
||||
@@ -1418,29 +1347,6 @@ spec:
|
||||
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
|
||||
|
||||
@@ -318,13 +318,6 @@ spec:
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
metadata:
|
||||
properties:
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
orderedResources:
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
# `velero debug` command for gathering troubleshooting information
|
||||
|
||||
## Abstract
|
||||
To simplify the communication between velero users and developers, this document proposes the `velero debug` command to generate a tarball including the logs needed for debugging.
|
||||
|
||||
Github issue: https://github.com/vmware-tanzu/velero/issues/675
|
||||
|
||||
## Background
|
||||
Gathering information to troubleshoot a Velero deployment is currently spread across multiple commands, and is not very efficient. Logs for the Velero server itself are accessed via a kubectl logs command, while information on specific backups or restores are accessed via a Velero subcommand. Restic logs are even more complicated to retrieve, since one must gather logs for every instance of the daemonset, and there’s currently no good mechanism to locate which node a particular restic backup ran against.
|
||||
A dedicated subcommand can lower this effort and reduce back-and-forth between user and developer for collecting the logs.
|
||||
|
||||
|
||||
## Goals
|
||||
- Enable efficient log collection for Velero and associated components, like plugins and restic.
|
||||
|
||||
## Non Goals
|
||||
- Collecting logs for components that do not belong to velero such as storage service.
|
||||
- Automated log analysis.
|
||||
|
||||
## High-Level Design
|
||||
With the introduction of the new command `velero debug`, the command would download all of the following information:
|
||||
- velero deployment logs
|
||||
- restic DaemonSet logs
|
||||
- plugin logs
|
||||
- All the resources in the group `velero.io` that are created such as:
|
||||
- Backup
|
||||
- Restore
|
||||
- BackupStorageLocation
|
||||
- PodVolumeBackup
|
||||
- PodVolumeRestore
|
||||
- *etc ...*
|
||||
- Log of the backup and restore, if specified in the param
|
||||
|
||||
A project called `crash-diagnostics` (or `crashd`) (https://github.com/vmware-tanzu/crash-diagnostics) implements the Kubernetes API queries and provides Starlark scripting language to abstract details, and collect the information into a local copy. It can be used as a standalone CLI executing a Starlark script file.
|
||||
With the capabilities of embedding files in Go 1.16, we can define a Starlark script gathering the necessary information, embed the script at build time, then the velero debug command will invoke `crashd`, passing in the script’s text contents.
|
||||
|
||||
## Detailed Design
|
||||
### Triggering the script
|
||||
The Starlark script to be called by crashd:
|
||||
|
||||
```python
|
||||
def capture_backup_logs(cmd, namespace):
|
||||
if args.backup:
|
||||
log("Collecting log and information for backup: {}".format(args.backup))
|
||||
backupDescCmd = "{} --namespace={} backup describe {} --details".format(cmd, namespace, args.backup)
|
||||
capture_local(cmd=backupDescCmd, file_name="backup_describe_{}.txt".format(args.backup))
|
||||
backupLogsCmd = "{} --namespace={} backup logs {}".format(cmd, namespace, args.backup)
|
||||
capture_local(cmd=backupLogsCmd, file_name="backup_{}.log".format(args.backup))
|
||||
def capture_restore_logs(cmd, namespace):
|
||||
if args.restore:
|
||||
log("Collecting log and information for restore: {}".format(args.restore))
|
||||
restoreDescCmd = "{} --namespace={} restore describe {} --details".format(cmd, namespace, args.restore)
|
||||
capture_local(cmd=restoreDescCmd, file_name="restore_describe_{}.txt".format(args.restore))
|
||||
restoreLogsCmd = "{} --namespace={} restore logs {}".format(cmd, namespace, args.restore)
|
||||
capture_local(cmd=restoreLogsCmd, file_name="restore_{}.log".format(args.restore))
|
||||
|
||||
ns = args.namespace if args.namespace else "velero"
|
||||
output = args.output if args.output else "bundle.tar.gz"
|
||||
cmd = args.cmd if args.cmd else "velero"
|
||||
# Working dir for writing during script execution
|
||||
crshd = crashd_config(workdir="./velero-bundle")
|
||||
set_defaults(kube_config(path=args.kubeconfig, cluster_context=args.kubecontext))
|
||||
log("Collecting velero resources in namespace: {}". format(ns))
|
||||
kube_capture(what="objects", namespaces=[ns], groups=['velero.io'])
|
||||
capture_local(cmd="{} version -n {}".format(cmd, ns), file_name="version.txt")
|
||||
log("Collecting velero deployment logs in namespace: {}". format(ns))
|
||||
kube_capture(what="logs", namespaces=[ns])
|
||||
capture_backup_logs(cmd, ns)
|
||||
capture_restore_logs(cmd, ns)
|
||||
archive(output_file=output, source_paths=[crshd.workdir])
|
||||
log("Generated debug information bundle: {}".format(output))
|
||||
```
|
||||
The sample command to trigger the script via crashd:
|
||||
```shell
|
||||
./crashd run ./velero.cshd --args
|
||||
'backup=harbor-backup-2nd,namespace=velero,basedir=,restore=,kubeconfig=/home/.kube/minikube-250-224/config,output='
|
||||
```
|
||||
To trigger the script in `velero debug`, in the package `pkg/cmd/cli/debug` a struct `option` will be introduced
|
||||
```go
|
||||
type option struct {
|
||||
// currCmd the velero command
|
||||
currCmd string
|
||||
// workdir for crashd will be $baseDir/velero-debug
|
||||
baseDir string
|
||||
// the namespace where velero server is installed
|
||||
namespace string
|
||||
// the absolute path for the log bundle to be generated
|
||||
outputPath string
|
||||
// the absolute path for the kubeconfig file that will be read by crashd for calling K8S API
|
||||
kubeconfigPath string
|
||||
// the kubecontext to be used for calling K8S API
|
||||
kubeContext string
|
||||
// optional, the name of the backup resource whose log will be packaged into the debug bundle
|
||||
backup string
|
||||
// optional, the name of the restore resource whose log will be packaged into the debug bundle
|
||||
restore string
|
||||
// optional, it controls whether to print the debug log messages when calling crashd
|
||||
verbose bool
|
||||
}
|
||||
```
|
||||
The code will consolidate the input parameters and execution context of the `velero` CLI to form the option struct, which can be transformed into the `argsMap` that can be used when calling the func `exec.Execute` in `crashd`:
|
||||
https://github.com/vmware-tanzu/crash-diagnostics/blob/v0.3.4/exec/executor.go#L17
|
||||
|
||||
## Alternatives Considered
|
||||
The collection could be done via the kubernetes client-go API, but such integration is not necessarily trivial to implement, therefore, `crashd` is preferred approach
|
||||
|
||||
## Security Considerations
|
||||
- The starlark script will be embedded into the velero binary, and the byte slice will be passed to the `exec.Execute` func directly, so there’s little risk that the script will be modified before being executed.
|
||||
|
||||
## Compatibility
|
||||
As the `crashd` project evolves the behavior of the internal functions used in the Starlark script may change. We’ll ensure the correctness of the script via regular E2E tests.
|
||||
|
||||
|
||||
## Implementation
|
||||
1. Bump up to use Go v1.16 to compile velero
|
||||
2. Embed the starlark script
|
||||
3. Implement the `velero debug` sub-command to call the script
|
||||
4. Add E2E test case
|
||||
|
||||
## Open Questions
|
||||
- **Command dependencies:** In the Starlark script, for collecting version info and backup logs, it calls the `velero backup logs` and `velero version`, which makes the call stack like velero debug -> crashd -> velero xxx. We need to make sure this works under different PATH settings.
|
||||
- **Progress and error handling:** The log collection may take a relatively long time, log messages should be printed to indicate the progress when different items are being downloaded and packaged. Additionally, when an error happens, `crashd` may omit some errors, so before the script is executed we'll do some validation and make sure the `debug` command fail early if some parameters are incorrect.
|
||||
|
Before Width: | Height: | Size: 81 KiB |
@@ -1,219 +0,0 @@
|
||||
# Object Graph Manifest for Velero
|
||||
|
||||
## Abstract
|
||||
|
||||
One to two sentences that describes the goal of this proposal and the problem being solved by the proposed change.
|
||||
The reader should be able to tell by the title, and the opening paragraph, if this document is relevant to them.
|
||||
|
||||
Currently, Velero does not have a complete manifest of everything in the backup, aside from the backup tarball itself.
|
||||
This change introduces a new data structure to be stored with a backup in object storage which will allow for more efficient operations in reporting of what a backup contains.
|
||||
Additionally, this manifest should enable advancements in Velero's features and architecture, enabling dry-run support, concurrent backup and restore operations, and reliable restoration of complex applications.
|
||||
|
||||
## Background
|
||||
|
||||
Right now, Velero backs up items one at a time, sorted by API Group and namespace.
|
||||
It also restores items one at a time, using the restoreResourcePriorities flag to indicate which order API Groups should have their objects restored first.
|
||||
While this does work currently, it presents challenges for more complex applications that have their dependencies in the form of a graph rather than strictly linear.
|
||||
|
||||
For example, Cluster API clusters are a set of complex Kubernetes objects that require that the "root" objects are restored first, before their "leaf" objects.
|
||||
If a Cluster that a ClusterResourceSetBinding refers to does not exist, then a restore of the CAPI cluster will fail.
|
||||
|
||||
Additionally, Velero does not have a reliable way to communicate what objects will be affected in a backup or restore operation without actually performing the operation.
|
||||
This complicates dry-run tasks, because a user must simply perform the action without knowing what will be touched.
|
||||
It also complicates allowing backups and restores to run in parallel, because there is currently no way to know if a single Kubernetes object is included in multiple backups or restores, which can lead to unreliability, deadlocking, and race conditions were Velero made to be more concurrent today.
|
||||
|
||||
## Goals
|
||||
|
||||
- Introduce a manifest data structure that defines the contents of a backup.
|
||||
- Store the manifest data into object storage alongside existing backup data.
|
||||
|
||||
## Non Goals
|
||||
|
||||
This proposal seeks to enable, but not define, the following.
|
||||
|
||||
- Implementing concurrency beyond what already exists in Velero.
|
||||
- Implementing a dry-run feature.
|
||||
- Implementing a new restore ordering procedure.
|
||||
|
||||
While the data structure should take these scenarios into account, they will not be implemented alongside it.
|
||||
|
||||
## High-Level Design
|
||||
|
||||
To uniquely identify a Kubernetes object within a cluster or backup, the following fields are sufficient:
|
||||
|
||||
- API Group and Version (example: backup.velero.io/v1)
|
||||
- Namespace
|
||||
- Name
|
||||
- Labels
|
||||
|
||||
This criteria covers the majority of Velero's inclusion or exclusion logic.
|
||||
However, some additional fields enable further use cases.
|
||||
|
||||
- Owners, which are other Kubernetes objects that have some relationship to this object. They may be strict or soft dependencies.
|
||||
- Annotations, which provide extra metadata about the object that might be useful for other programs to consume.
|
||||
- UUID generated by Kubernetes. This is useful in defining Owner relationships, providing a single, immutable key to find an object. This is _not_ considered at restore time, only internally for defining links.
|
||||
|
||||
All of this information already exists within a Velero backup's tarball of resources, but extracting such data is inefficient.
|
||||
The entire tarball must be downloaded and extracted, and then JSON within parsed to read labels, owners, annotations, and a UUID.
|
||||
The rest of the information is encoded in the file system structure within the Velero backup tarball.
|
||||
While doable, this is heavyweight in terms of time and potentially memory.
|
||||
|
||||
Instead, this proposal suggests adding a new manifest structure that is kept alongside the backup tarball.
|
||||
This structure would contain the above fields only, and could be used to perform inclusion/exclusion logic on a backup, select a resource from within a backup, and do set operations over backup or restore contents to identify overlapping resources.
|
||||
|
||||
Here are some use cases that this data structure should enable, that have been difficult to implement prior to its existence:
|
||||
|
||||
- A dry-run operation on backup, informing the user what would be selected if they were to perform the operation.
|
||||
A manifest could be created and saved, allowing for a user to do a dry-run, then accept it to perform the backup.
|
||||
Restore operations can be treated similarly.
|
||||
- Efficient, non-overlapping parallelization of backup and restore operations.
|
||||
By building or reading a manifest before performing a backup or restore, Velero can determine if there are overlapping resources.
|
||||
If there are no overlaps, the operations can proceed in parallel.
|
||||
If there are overlaps, the operations can proveed serially.
|
||||
- Graph-based restores for non-linear dependencies.
|
||||
Not all resources in a Kubernetes cluster can be defined in a strict, linear way.
|
||||
They may have multiple owners, and writing BackupItemActions or RestoreItemActions to simply return a chain of owners is not an efficient way to support the many Kubernetes operators/controllers being written.
|
||||
Instead, by having a manifest with enough information, Velero can build a discrete list that ensures dependencies are restored before their dependents, with less input from plugin authors.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
The Manifest data structure would look like this, in Go type structure:
|
||||
|
||||
```golang
|
||||
// NamespacedItems maps a given namespace to all of its contained items.
|
||||
type NamespacedItems map[string]*Item
|
||||
|
||||
// APIGroupNamespaces maps an API group/version to a map of namespaces and their items.
|
||||
type KindNamespaces map[string]NamespacedItems
|
||||
|
||||
type Manifest struct {
|
||||
// Kinds holds the top level map of all resources in a manifest.
|
||||
Kinds KindNamespaces
|
||||
|
||||
// Index is used to look up an individual item quickly based on UUID.
|
||||
// This enables fetching owners out of the maps more efficiently at the cost of memory space.
|
||||
Index map[string]*Item
|
||||
}
|
||||
|
||||
|
||||
// Item represents a Kubernetes resource within a backup based on it's selectable criteria.
|
||||
// It is not the whole Kubernetes resource as retrieved from the API server, but rather a collection of important fields needed for filtering.
|
||||
type Item struct {
|
||||
// Kubernetes API group which this Item belongs to.
|
||||
// Could be a core resource, or a CustomResourceDefinition.
|
||||
APIGroup string
|
||||
|
||||
// Version of the APIGroup that the Item belongs to.
|
||||
APIVersion string
|
||||
|
||||
// Kubernetes namespace which contains this item.
|
||||
// Empty string for cluster-level resource.
|
||||
Namespace string
|
||||
|
||||
// Item's given name.
|
||||
Name string
|
||||
|
||||
// Map of labels that the Item had at backup time.
|
||||
Labels map[string]string
|
||||
|
||||
// Map of annotations that the Item had at Backup time.
|
||||
// Useful for plugins that may decide to process only Items with specific annotations.
|
||||
Annotations map[string]string
|
||||
|
||||
// Owners is a list of UUIDs to other items that own or refer to this item.
|
||||
Owners []string
|
||||
|
||||
// Manifest is a pointer to the Manifest in which this object is contained.
|
||||
// Useful for getting access to things like the Manifest.Index map.
|
||||
Manifest *Manifest
|
||||
}
|
||||
```
|
||||
|
||||
In addition to the new types, the following Go interfaces would be provided for convenience.
|
||||
|
||||
```golang
|
||||
type Itermer interface {
|
||||
// Returns the Item as a string, following the current Velero backup version 1.1.0 tarball structure format.
|
||||
// <APIGroup>/<Namespace>/<APIVersion>/<name>.json
|
||||
String() string
|
||||
|
||||
// Owners returns a slice of realized Items that own or refer to the current Item.
|
||||
// Useful for building out a full graph of Items to restore.
|
||||
// Will use the UUIDs in Item.Owners to look up the owner Items in the Manifest.
|
||||
Owners() []*Item
|
||||
|
||||
// Kind returns the Kind of an object, which is a combination of the APIGroup and APIVersion.
|
||||
// Useful for verifying the needed CustomResourceDefinition exists before actually restoring this Item.
|
||||
Kind() *Item
|
||||
|
||||
// Children returns a slice of all Items that refer to this item as an Owner.
|
||||
Children() []*Items
|
||||
}
|
||||
|
||||
// This error type is being created in order to make reliable sentinel errors.
|
||||
// See https://dave.cheney.net/2019/06/10/constant-time for more details.
|
||||
type ManifestError string
|
||||
|
||||
func (e ManifestError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
const ItemAlreadyExists = ManifestError("item already exists in manifest")
|
||||
|
||||
type Manifester interface {
|
||||
// Set returns the entire list of resources as a set of strings (using Itemer.String).
|
||||
// This is useful for comparing two manifests and determining if they have any overlapping resources.
|
||||
// In the future, when implementing concurrent operations, this can be used as a sanity check to ensure resources aren't being backed up or restored by two operations at once.
|
||||
Set() sets.String
|
||||
|
||||
// Adds an item to the appropriate APIGroup and Namespace within a Manifest
|
||||
// Returns (true, nil) if the Item is successfully added to the Manifest,
|
||||
// Returns (false, ItemAlreadyExists) if the Item is already in the Manifest.
|
||||
Add(*Item) (bool, error)
|
||||
}
|
||||
```
|
||||
|
||||
### Serialization
|
||||
|
||||
The entire `Manifest` should be serialized into the `manifest.json` file within the object storage for a single backup.
|
||||
It is possible that this file could also be compressed for space efficiency.
|
||||
|
||||
### Memory Concerns
|
||||
|
||||
Because the `Manifest` is holding a minimal amount of data, memory sizes should not be a concern for most clusters.
|
||||
TODO: Document known limits on API group name, resource name, and kind name character limits.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
Introducing this manifest does not increase the attack surface of Velero, as this data is already present in the existing backups.
|
||||
Storing the manifest.json file next to the existing backup data in the object storage does not change access patterns.
|
||||
|
||||
## Compatibility
|
||||
|
||||
The introduction of this file should trigger Velero backup version 1.2.0, but it will not interfere with Velero versions that do not support the `Manifest` as the file will be additive.
|
||||
In time, this file will replace the `<backupname>-resource-list.json.gz` file, but for compatibility the two will appear side by side.
|
||||
|
||||
When first implemented, Velero should simply build the `Manifest` as it backs up items, and serialize it at the end.
|
||||
Any logic changes that rely on the `Manifest` file must be introduced with their own design document, with their own compatibility concerns.
|
||||
|
||||
## Implementation
|
||||
|
||||
The `Manifest` object will _not_ be implemented as a Kubernetes CustomResourceDefinition, but rather one of Velero's own internal constructs.
|
||||
|
||||
Implementation for the data structure alone should be minimal - the types will need to be defined in a `manifest` package.
|
||||
Then, the backup process should create a `Manifest`, passing it to the various `*Backuppers` in the `backup` package.
|
||||
These methods will insert individual `Items` into the `Manifest`.
|
||||
Finally, logic should be added to the `persistence` package to ensure that the new `manifest.json` file is uploadable and allowed.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
None so far.
|
||||
|
||||
## Open Issues
|
||||
|
||||
- When should compatibility with the `<backupname>-resource-list.json.gz` file be dropped?
|
||||
- What are some good test case Kubernetes resources and controllers to try this out with?
|
||||
Cluster API seems like an obvious choice, but are there others?
|
||||
- Since it is not implemented as a CustomResourceDefinition, how can a `Manifest` be retained so that users could issue a dry-run command, then perform their actual desire operation?
|
||||
Could it be stored in Velero's temp directories?
|
||||
Note that this is making Velero itself more stateful.
|
||||
@@ -8,19 +8,15 @@ This makes it so switching from one plugin to another necessitates overriding th
|
||||
|
||||
- To allow Velero to create and store multiple secrets for provider credentials, even multiple credentials for the same provider
|
||||
- To improve the UX for configuring the velero deployment with multiple plugins/providers.
|
||||
- Enable use cases such as AWS volume snapshots w/Minio as the object storage
|
||||
- Continue to support use cases where multiple Backup Storage Locations are in use simultaneously
|
||||
- `velero backup logs` while backup/restore is running
|
||||
- Handle changes in configuration while operations are happening as well as they currently are
|
||||
|
||||
## Non Goals
|
||||
|
||||
- To make any change except what's necessary to handle multiple credentials
|
||||
- To allow multiple credentials for or change the UX for node-based authentication (e.g. AWS IAM, GCP Workload Identity, Azure AAD Pod Identity). Node-based authentication will not allow cases such as a mix of AWS snapshots with Minio object storage.
|
||||
- To allow multiple credentials for or change the UX for node-based authentication (e.g. AWS IAM, GCP Workload Identity, Azure AAD Pod Identity).
|
||||
|
||||
## Design overview
|
||||
|
||||
Instead of one credential per Velero deployment, multiple credentials can be added and used with different BSLs.
|
||||
Instead of one credential per Velero deployment, multiple credentials can be added and used with different BSLs VSLs.
|
||||
|
||||
There are two aspects to handling multiple credentials:
|
||||
|
||||
@@ -34,98 +30,36 @@ Each of these aspects will be discussed in turn.
|
||||
Currently, Velero creates a secret (`cloud-credentials`) during install with a single entry that contains the contents of the credentials file passed by the user.
|
||||
|
||||
Instead of adding new CLI options to Velero to create and manage credentials, users will create their own Kubernetes secrets within the Velero namespace and reference these.
|
||||
This approach is being chosen as it allows users to directly manage Kubernetes secrets objects as they wish and it removes the need for wrapper functions to be created within Velero to manage the creation of secrets. Separate credentials rather than combining credentials in a single secret also avoids issues with maximum size of credentials as well as update in place issues.
|
||||
This approach is being chosen as it allows users to directly manage Kubernetes secrets objects as they wish and it removes the need for wrapper functions to be created within Velero to manage the creation of secrets.
|
||||
An initial approach to this problem included modifying the existing `cloud-credentials` secret to add a new entry with each new set of credentials.
|
||||
It is likely that this approach would encounter problems as users added more credentials as the maximum size of Secret in Kubernetes is 1MB.
|
||||
By allowing users to create Secrets as they need to, we remove these potential limitations.
|
||||
|
||||
To enable the use of existing Kubernetes secrets, BSLs will be modified to have a new field `Credential`.
|
||||
This field will be a [`SecretKeySelector`](https://godoc.org/k8s.io/api/core/v1#SecretKeySelector) which will enable the user to specify which key within a particular secret the BSL should use.
|
||||
To enable the use of existing Kubernetes secrets, BSLs and VSLs will be modified to have a new field `Credential`.
|
||||
This field will be a [`SecretKeySelector`](https://godoc.org/k8s.io/api/core/v1#SecretKeySelector) which will enable the user to specify which key within a particular secret the BSL/VSL should use.
|
||||
|
||||
Existing BackupStorageLocationSpec definition:
|
||||
|
||||
// BackupStorageLocationSpec defines the desired state of a Velero BackupStorageLocation
|
||||
type BackupStorageLocationSpec struct {
|
||||
// Provider is the provider of the backup storage.
|
||||
Provider string `json:"provider"`
|
||||
|
||||
// Config is for provider-specific configuration fields.
|
||||
// +optional
|
||||
Config map[string]string `json:"config,omitempty"`
|
||||
|
||||
StorageType `json:",inline"`
|
||||
|
||||
// Default indicates this location is the default backup storage location.
|
||||
// +optional
|
||||
Default bool `json:"default,omitempty"`
|
||||
|
||||
// AccessMode defines the permissions for the backup storage location.
|
||||
// +optional
|
||||
AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"`
|
||||
|
||||
// BackupSyncPeriod defines how frequently to sync backup API objects from object storage. A value of 0 disables sync.
|
||||
// +optional
|
||||
// +nullable
|
||||
BackupSyncPeriod *metav1.Duration `json:"backupSyncPeriod,omitempty"`
|
||||
|
||||
// ValidationFrequency defines how frequently to validate the corresponding object storage. A value of 0 disables validation.
|
||||
// +optional
|
||||
// +nullable
|
||||
ValidationFrequency *metav1.Duration `json:"validationFrequency,omitempty"`
|
||||
}
|
||||
|
||||
The following field will be added:
|
||||
|
||||
Credential *corev1api.SecretKeySelector `json:"credential,omitempty"`
|
||||
|
||||
The resulting BackupStorageLocationSpec will be this:
|
||||
|
||||
// BackupStorageLocationSpec defines the desired state of a Velero BackupStorageLocation
|
||||
type BackupStorageLocationSpec struct {
|
||||
// Provider is the provider of the backup storage.
|
||||
Provider string `json:"provider"`
|
||||
|
||||
// Config is for provider-specific configuration fields.
|
||||
// +optional
|
||||
Config map[string]string `json:"config,omitempty"`
|
||||
|
||||
// Credential contains the credential information intended to be used with this location
|
||||
// +optional
|
||||
Credential *corev1api.SecretKeySelector `json:"credential,omitempty"`
|
||||
|
||||
StorageType `json:",inline"`
|
||||
|
||||
// Default indicates this location is the default backup storage location.
|
||||
// +optional
|
||||
Default bool `json:"default,omitempty"`
|
||||
|
||||
// AccessMode defines the permissions for the backup storage location.
|
||||
// +optional
|
||||
AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"`
|
||||
|
||||
// BackupSyncPeriod defines how frequently to sync backup API objects from object storage. A value of 0 disables sync.
|
||||
// +optional
|
||||
// +nullable
|
||||
BackupSyncPeriod *metav1.Duration `json:"backupSyncPeriod,omitempty"`
|
||||
|
||||
// ValidationFrequency defines how frequently to validate the corresponding object storage. A value of 0 disables validation.
|
||||
// +optional
|
||||
// +nullable
|
||||
ValidationFrequency *metav1.Duration `json:"validationFrequency,omitempty"`
|
||||
}
|
||||
|
||||
The CLI for managing Backup Storage Locations (BSLs) will be modified to allow the user to set these credentials.
|
||||
Both `velero backup-location (create|set)` will have a new flag (`--credential`) to specify the secret and key within the secret to use.
|
||||
The CLI for managing BSLs and VSLs will be modified to allow the user to set these credentials.
|
||||
Both `velero backup-location (create|set)` and `velero snapshot-location (create|set)` will have a new flag (`--credential`) to specify the secret and key within the secret to use.
|
||||
This flag will take a key-value pair in the format `<secret-name>=<key-in-secret>`.
|
||||
The arguments will be validated to ensure that the secret exists in the Velero namespace.
|
||||
If the Credential field is empty in a BSL, the default credentials from `cloud-credentials` will be used as they
|
||||
are currently.
|
||||
|
||||
### Making credentials available to plugins
|
||||
|
||||
The approach we have chosen is to include the path to the credentials file in the `config` map passed to a plugin.
|
||||
There are three different approaches that can be taken to provide credentials to plugin processes:
|
||||
|
||||
1. Providing the path to the credentials file as an environment variable per plugin. This is how credentials are currently passed.
|
||||
1. Include the path to the credentials file in the `config` map passed to a plugin.
|
||||
1. Include the details of the secret in the `config` map passed to a plugin.
|
||||
|
||||
The last two options require changes to the plugin as the plugin will need to instantiate a client using the provided credentials.
|
||||
The client libraries used by the plugins will not be able to rely on the credentials details being available in the environment as they currently do.
|
||||
|
||||
We have selected option 2 as the approach to take which will be described below.
|
||||
|
||||
### Including the credentials file path in the `config` map
|
||||
|
||||
Prior to using any secret for a BSL, it will need to be serialized to disk.
|
||||
Using the details in the `Credential` field in the BSL, the contents of the Secret will be read and serialized.
|
||||
Prior to using any secret for a BSL or VSL, it will need to be serialized to disk.
|
||||
Using the details in the `Credential` field in the BSL/VSL, the contents of the Secret will be read and serialized.
|
||||
To achieve this, we will create a new package, `credentials`, which will introduce new types and functions to manage the fetching of credentials based on a `SecretKeySelector`.
|
||||
This will also be responsible for serializing the fetched credentials to a temporary directory on the Velero pod filesystem.
|
||||
|
||||
@@ -137,8 +71,8 @@ This means that any time a `BackupStore`, or other type which requires credentia
|
||||
If we instead wanted to use an unique file each time, we could work around the of multiple files being written by cleaning up the temporary files upon completion of the plugin operations, if this information is known.
|
||||
|
||||
Once the credentials have been serialized, this path will be made available to the plugins.
|
||||
Instead of setting the necessary environment variable for the plugin process, the `config` map for the BSL will be modified to include an addiitional entry with the path to the credentials file: `credentialsFile`.
|
||||
This will be passed through when [initializing the BSL](https://github.com/vmware-tanzu/velero/blob/main/pkg/plugin/velero/object_store.go#L27-L30) and it will be the responsibility of the plugin to use the passed credentials when starting a session.
|
||||
Instead of setting the necessary environment variable for the plugin process, the `config` map for the BSL/VSL will be modified to include an addiitional entry with the path to the credentials file: `credentialsFile`.
|
||||
This will be passed through when [initializing the BSL/VSL](https://github.com/vmware-tanzu/velero/blob/main/pkg/plugin/velero/object_store.go#L27-L30) and it will be the responsibility of the plugin to use the passed credentials when starting a session.
|
||||
For an example of how this would affect the AWS plugin, see [this PR](https://github.com/vmware-tanzu/velero-plugin-for-aws/pull/69).
|
||||
|
||||
The restic controllers will also need to be updated to use the correct credentials.
|
||||
@@ -150,22 +84,6 @@ Currently, GCP is the only provider that relies on the existing environment vari
|
||||
For GCP, the environment variable will be overwritten with the path of the serialized secret.
|
||||
|
||||
|
||||
## Split credentials between VolumeSnapshotter and ObjectStore plugins
|
||||
|
||||
One of the use cases we wish to satisfy is the ability to specify a different object store than the cloud provider offers,
|
||||
for example, using a Minio S3 object store from within AWS. Currently the VolumeSnapshotter and the ObjectStore plugin
|
||||
share the cloud credentials. Each backup/restore has a BackupStorageLocation associated
|
||||
with it. The BackupStorageLocation can optionally specify the credential used by the ObjectStorePlugin and Restic daemons
|
||||
while the cloud credential will always be used for the VolumeSnapshotter.
|
||||
|
||||
## Velero Plugin for vSphere compatibility
|
||||
|
||||
The vSphere plugin is implemented as a BackupItemAction and shares the credentials of the AWS plug-in for S3 access.
|
||||
The backup storage location is passed in _Backup.Spec.StorageLocation_. Currently the plugin retrieves the S3 bucket and
|
||||
server from the BSL and creates a BackupRespositoryClaim with that and the credentials retrieved from the cloud credential.
|
||||
The plug-in will need to be modified to retrieve the credentials field from the BSL and use that credential in the
|
||||
BackupRepositoryClaim.
|
||||
|
||||
## Backwards compatibility
|
||||
|
||||
For now, regardless of the approaches used above, we will still support the existing workflow.
|
||||
@@ -174,33 +92,15 @@ Users will be able to set credentials during install and a secret will be create
|
||||
This secret will still be mounted into the Velero pods and the appropriate environment variables set.
|
||||
This will allow users to use versions of plugins which haven't yet been updated to use credentials directly, such as with many community created plugins.
|
||||
|
||||
Multiple credential handling will only be used in the case where a particular BSL has been modified to use an existing secret.
|
||||
Multiple credential handling will only be used in the case where a particular BSL/VSL has been modified to use an existing secret.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
Although the handling of secrets will be similar to how credentials are currently managed within Velero, care must be taken to ensure that any new code does not leak the contents of secrets, for example, including them within logs.
|
||||
|
||||
## Parallelism
|
||||
In order to support parallelism, Velero will need to be able to use multiple credentials simultaneously with the
|
||||
ObjectStore. Currently backups are single threaded and a single BSL will be used throughout the entire backup. The only
|
||||
existing points of parallelism are when a user downloads logs for a backup or the BackupStorageLocationReconciler
|
||||
reconciles while a backup or restore is running. In the current code, `download_request_controller.go` and
|
||||
`backup_storage_location_controller.go` create a new plug-in manager and hence another ObjectStore plugin in
|
||||
parallel with the ObjectStore plugin servicing a backup or restore (if one is running).
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
Three different approaches can be taken to provide credentials to plugin processes:
|
||||
|
||||
1. Providing the path to the credentials file as an environment variable per plugin. This is how credentials are currently passed.
|
||||
1. Include the path to the credentials file in the `config` map passed to a plugin.
|
||||
1. Include the details of the secret in the `config` map passed to a plugin.
|
||||
|
||||
The last two options require changes to the plugin as the plugin will need to instantiate a client using the provided credentials.
|
||||
The client libraries used by the plugins will not be able to rely on the credentials details being available in the environment as they currently do.
|
||||
|
||||
We have selected option 2 as the approach to take.
|
||||
|
||||
As mentioned above, there were three potential approaches for providing this support.
|
||||
The approaches that were not selected are detailed below for reference.
|
||||
|
||||
#### Providing the credentials via environment variables
|
||||
@@ -210,11 +110,11 @@ Currently, there is a single secret, which is mounted into every pod deployed by
|
||||
This path is made known to all plugins through provider specific environment variables and all possible provider environment variables are set to this path.
|
||||
|
||||
Instead of setting the environment variables for all the pods, we can modify plugin processes are created so that the environment variables are set on a per plugin process basis.
|
||||
Prior to using any secret for a BSL, it will need to be serialized to disk.
|
||||
Using the details in the `Credential` field in the BSL, the contents of the Secret will be read and serialized to a file.
|
||||
Prior to using any secret for a BSL or VSL, it will need to be serialized to disk.
|
||||
Using the details in the `Credential` field in the BSL/VSL, the contents of the Secret will be read and serialized to a file.
|
||||
Each plugin process would still have the same set of environment variables set, however the value used for each of these variables would instead be the path to the serialized secret.
|
||||
|
||||
To set the environment variables for a plugin process, the plugin manager must be modified so that when creating an ObjectStore, we pass in the entire BSL object, rather than [just the provider](https://github.com/vmware-tanzu/velero/blob/main/pkg/plugin/clientmgmt/manager.go#L132-L158).
|
||||
To set the environment variables for a plugin process, the plugin manager must be modified so that when creating an ObjectStore or VolumeSnapshotter, we pass in the entire BSL/VSL object, rather than [just the provider](https://github.com/vmware-tanzu/velero/blob/main/pkg/plugin/clientmgmt/manager.go#L132-L158).
|
||||
The plugin manager currently stores a map of [plugin executables to an associated `RestartableProcess`](https://github.com/vmware-tanzu/velero/blob/main/pkg/plugin/clientmgmt/manager.go#L59-L70).
|
||||
New restartable processes are created only [with the executable that the process would run](https://github.com/vmware-tanzu/velero/blob/main/pkg/plugin/clientmgmt/manager.go#L122).
|
||||
This could be modified to also take the necessary environment variables so that when [underlying go-plugin process is created](https://github.com/vmware-tanzu/velero/blob/main/pkg/plugin/clientmgmt/client_builder.go#L78), these environment variables could be provided and would be set on the plugin process.
|
||||
|
||||
@@ -1,311 +0,0 @@
|
||||
# Upload Progress Monitoring
|
||||
|
||||
Volume snapshotter plug-in are used by Velero to take snapshots of persistent volume contents.
|
||||
Depending on the underlying storage system, those snapshots may be available to use immediately,
|
||||
they may be uploaded to stable storage internally by the plug-in or they may need to be uploaded after
|
||||
the snapshot has been taken. We would like for Velero to continue on to the next part of the backup as quickly
|
||||
as possible but we would also like the backup to not be marked as complete until it is a usable backup. We'd also
|
||||
eventually like to bring the control of upload under the control of Velero and allow the user to make decisions
|
||||
about the ultimate destination of backup data independent of the storage system they're using.
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
AWS - AWS snapshots return quickly, but are then uploaded in the background and cannot be used until EBS moves
|
||||
the data into S3 internally.
|
||||
|
||||
vSphere - The vSphere plugin takes a local snapshot and then the vSphere plugin uploads the data to S3. The local
|
||||
snapshot is usable before the upload completes.
|
||||
|
||||
Restic - Does not go through the volume snapshot path. Restic backups will block Velero progress until completed.
|
||||
|
||||
## Goals
|
||||
|
||||
- Enable monitoring of operations that continue after snapshotting operations have completed
|
||||
- Keep non-usable backups (upload/persistence has not finished) from appearing as completed
|
||||
- Minimize change to volume snapshot and BackupItemAction plug-ins
|
||||
|
||||
## Non-goals
|
||||
- Unification of BackupItemActions and VolumeSnapshotters
|
||||
|
||||
## Models
|
||||
|
||||
### Internal configuration and management
|
||||
In this model, movement of the snapshot to stable storage is under the control of the snapshot
|
||||
plug-in. Decisions about where and when the snapshot gets moved to stable storage are not
|
||||
directly controlled by Velero. This is the model for the current VolumeSnapshot plugins.
|
||||
|
||||
### Velero controlled management
|
||||
In this model, the snapshot is moved to external storage under the control of Velero. This
|
||||
enables Velero to move data between storage systems. This also allows backup partners to use
|
||||
Velero to snapshot data and then move the data into their backup repository.
|
||||
|
||||
## Backup phases
|
||||
|
||||
Velero currently has backup phases "InProgress" and "Completed". The backup moves to the Completed
|
||||
phase when all of the volume snapshots have completed and the Kubernetes metadata has been written
|
||||
into the object store. However, the actual data movement may be happening in the background
|
||||
after the backup has been marked "Completed". The backup is not actually a stable backup until
|
||||
the data has been persisted properly. In some cases (e.g. AWS) the backup cannot be restored from
|
||||
until the snapshots have been persisted.
|
||||
|
||||
Once the snapshots have been taken, however, it is possible for additional backups to be made without
|
||||
interference. Waiting until all data has been moved before starting the next backup will
|
||||
slow the progress of the system without adding any actual benefit to the user.
|
||||
|
||||
A new backup phase, "Uploading" will be introduced. When a backup has entered this phase, Velero
|
||||
is free to start another backup. The backup will remain in the "Uploading" phase until all data
|
||||
has been successfully moved to persistent storage. The backup will not fail once it reaches
|
||||
this phase, it will continuously retry moving the data. If the backup is deleted (cancelled), the plug-ins will
|
||||
attempt to delete the snapshots and stop the data movement - this may not be possible with all
|
||||
storage systems.
|
||||
|
||||
### State progression
|
||||
|
||||

|
||||
### New
|
||||
When a backup request is initially created, it is in the "New" phase.
|
||||
|
||||
The next state is either "InProgress" or "FailedValidation"
|
||||
|
||||
### FailedValidation
|
||||
If the backup request is incorrectly formed, it goes to the "FailedValidation" phase and terminates
|
||||
|
||||
### InProgress
|
||||
When work on the backup begins, it moves to the "InProgress" phase. It remains in the "InProgress"
|
||||
phase until all pre/post execution hooks have been executed, all snapshots have been taken and the
|
||||
Kubernetes metadata and backup info is safely written to the object store plug-in.
|
||||
|
||||
In the current implementation, Restic backups will move data during the "InProgress" phase.
|
||||
In the future, it may be possible to combine a snapshot with a Restic (or equivalent) backup which
|
||||
would allow for data movement to be handled in the "Uploading" phase,
|
||||
|
||||
The next phase is either "Completed", "Uploading", "Failed" or "PartiallyFailed". Backups which
|
||||
would have a final phase of "Completed" or "PartiallyFailed" may move to the "Uploading" state.
|
||||
A backup which will be marked "Failed" will go directly to
|
||||
the "Failed" phase. Uploads may continue in the background for snapshots that were taken by a "Failed"
|
||||
backup, but no progress will not be monitored or updated. When a "Failed" backup is deleted, all snapshots
|
||||
will be deleted and at that point any uploads still in progress should be aborted.
|
||||
|
||||
### Uploading (new)
|
||||
The "Uploading" phase signifies that the main part of the backup, including snapshotting has completed successfully
|
||||
and and uploading is continuing. In the event of an error during uploading, the phase will change to
|
||||
UploadingPartialFailure. On success, the phase changes to Completed. The backup cannot be
|
||||
restored from when it is in the Uploading state.
|
||||
|
||||
### UploadingPartialFailure (new)
|
||||
The "UploadingPartialFailure" phase signifies that the main part of the backup, including snapshotting has completed,
|
||||
but there were partial failures either during the main part or during the uploading. The backup cannot be
|
||||
restored from when it is in the UploadingPartialFailure state.
|
||||
|
||||
### Failed
|
||||
When a backup has had fatal errors it is marked as "Failed" This backup cannot be restored from.
|
||||
|
||||
### Completed
|
||||
The "Completed" phase signifies that the backup has completed, all data has been transferred to stable storage
|
||||
and the backup is ready to be used in a restore. When the Completed phase has been reached it is safe
|
||||
to remove any of the items that were backed up.
|
||||
|
||||
### PartiallyFailed
|
||||
The "PartiallyFailed" phase signifies that the backup has completed and at least part of the backup is usable.
|
||||
Restoration from a PartiallyFailed backup will not result in a complete restoration but pieces may be available.
|
||||
|
||||
## Workflow
|
||||
|
||||
When a BackupAction is executed, any SnapshotItemAction or VolumeSnapshot plugins will return snapshot IDs.
|
||||
The plugin should be able to provide status on
|
||||
the progress for the snapshot and handle cancellation of the upload if the snapshot is deleted.
|
||||
If the plugin is restarted, the snapshot ID should remain valid.
|
||||
|
||||
When all snapshots have been taken and Kubernetes resources have been persisted to the ObjectStorePlugin
|
||||
the backup will either have fatal errors or will be at least partially usable.
|
||||
|
||||
If the backup has fatal errors it will move to the "Failed" state and finish. If a backup fails, the upload will not be
|
||||
cancelled but it will not be monitored either. For backups in any phase, all snapshots will be deleted when the backup
|
||||
is deleted. Plugins will cancel any data movement and
|
||||
remove snapshots and other associated resources when the VolumeSnapshotter DeleteSnapshot method or
|
||||
DeleteItemAction Execute method is called.
|
||||
|
||||
Velero will poll the plugins for status on the snapshots when the backup exits the "InProgress" phase and
|
||||
has no fatal errors.
|
||||
|
||||
If any snapshots are not complete, the backup will move to either Uploading or UploadingPartialFailure or Failed.
|
||||
|
||||
Post-snapshot operations may take a long time and Velero and its plugins may be restarted during
|
||||
this time. Once a backup has moved into the Uploading or UploadingPartialFailure phase, another
|
||||
backup may be started.
|
||||
|
||||
While in the Uploading or UploadingPartialFailure phase, the snapshots and backup items will be periodically polled.
|
||||
When all of the snapshots and backup items have reported success, the backup will move to the Completed or
|
||||
PartiallyFailed phase, depending on whether the backup was in the Uploading or UploadingPartialFailure phase.
|
||||
|
||||
The Backup resources will not be written to object storage until the backup has entered a final phase:
|
||||
Completed, Failed or PartialFailure
|
||||
## Reconciliation of InProgress backups
|
||||
|
||||
InProgress backups will not have a `velero-backup.json` present in the object store. During reconciliation, backups which
|
||||
do not have a `velero-backup.json` object in the object store will be ignored.
|
||||
|
||||
## Plug-in API changes
|
||||
|
||||
### UploadProgress struct
|
||||
|
||||
type UploadProgress struct {
|
||||
completed bool // True when the operation has completed, either successfully or with a failure
|
||||
err error // Set when the operation has failed
|
||||
itemsCompleted, itemsToComplete int64 // The number of items that have been completed and the items to complete
|
||||
// For a disk, an item would be a byte and itemsToComplete would be the
|
||||
// total size to transfer (may be less than the size of a volume if
|
||||
// performing an incremental) and itemsCompleted is the number of bytes
|
||||
// transferred. On successful completion, itemsCompleted and itemsToComplete
|
||||
// should be the same
|
||||
started, updated time.Time // When the upload was started and when the last update was seen. Not all
|
||||
// systems retain when the upload was begun, return Time 0 (time.Unix(0, 0))
|
||||
// if unknown.
|
||||
}
|
||||
|
||||
### VolumeSnapshotter changes
|
||||
|
||||
A new method will be added to the VolumeSnapshotter interface (details depending on plug-in versioning spec)
|
||||
|
||||
UploadProgress(snapshotID string) (UploadProgress, error)
|
||||
|
||||
UploadProgress will report the current status of a snapshot upload. This should be callable at any time after the snapshot
|
||||
has been taken. In the event a plug-in is restarted, if the snapshotID continues to be valid it should be possible to
|
||||
retrieve the progress.
|
||||
|
||||
`error` is set if there is an issue retrieving progress. If the snapshot is has encountered an error during the upload,
|
||||
the error should be return in UploadProgress and error should be nil.
|
||||
|
||||
### SnapshotItemAction plug-in
|
||||
|
||||
Currently CSI snapshots and the Velero Plug-in for vSphere are implemented as BackupItemAction plugins. The majority of
|
||||
BackupItemAction plugins do not take snapshots or upload data so rather than modify BackupItemAction we introduce a new
|
||||
plug-ins, SnapshotItemAction. SnapshotItemAction will be used in place of BackupItemAction for
|
||||
the CSI snapshots and the Velero Plug-in for vSphere and will return a snapshot ID in addition to the item itself.
|
||||
|
||||
The SnapshotItemAction plugin identifier as well as the Item and Snapshot ID will be stored in the
|
||||
`<backup-name>-itemsnapshots.json.gz`. When checking for progress, this info will be used to select the appropriate
|
||||
SnapshotItemAction plugin to query for progress.
|
||||
|
||||
_NotApplicable_ should only be returned if the SnapshotItemAction plugin should not be handling the item. If the
|
||||
SnapshotItemAction plugin should handle the item but, for example, the item/snapshot ID cannot be found to report progress, a
|
||||
UploadProgress struct with the error set appropriately (in this case _NotFound_) should be returned.
|
||||
|
||||
// SnapshotItemAction is an actor that snapshots an individual item being backed up (it may also do other
|
||||
operations on the item that is returned).
|
||||
|
||||
type SnapshotItemAction interface {
|
||||
// AppliesTo returns information about which resources this action should be invoked for.
|
||||
// A BackupItemAction's Execute function will only be invoked on items that match the returned
|
||||
// selector. A zero-valued ResourceSelector matches all resources.
|
||||
AppliesTo() (ResourceSelector, error)
|
||||
|
||||
// Execute allows the ItemAction to perform arbitrary logic with the item being backed up,
|
||||
// including mutating the item itself prior to backup. The item (unmodified or modified)
|
||||
// should be returned, along with an optional slice of ResourceIdentifiers specifying
|
||||
// additional related items that should be backed up.
|
||||
Execute(item runtime.Unstructured, backup *api.Backup) (runtime.Unstructured, snapshotID string,
|
||||
[]ResourceIdentifier, error)
|
||||
|
||||
// Progress
|
||||
Progress(input *SnapshotItemProgressInput) (UploadProgress, error)
|
||||
}
|
||||
|
||||
// SnapshotItemProgressInput contains the input parameters for the SnapshotItemAction's Progress function.
|
||||
type SnapshotItemProgressInput struct {
|
||||
// Item is the item that was stored in the backup
|
||||
Item runtime.Unstructured
|
||||
// SnapshotID is the snapshot ID returned by SnapshotItemAction
|
||||
SnapshotID string
|
||||
// Backup is the representation of the restore resource processed by Velero.
|
||||
Backup *velerov1api.Backup
|
||||
}
|
||||
|
||||
|
||||
## Changes in Velero backup format
|
||||
|
||||
No changes to the existing format are introduced by this change. A `<backup-name>-itemsnapshots.json.gz` file will be
|
||||
added that contains the items and snapshot IDs returned by ItemSnapshotAction. Also, the creation of the
|
||||
`velero-backup.json` object will not occur until the backup moves to one of the terminal phases (_Completed_,
|
||||
_PartiallyFailed_, or _Failed_). Reconciliation should ignore backups that do not have a `velero-backup.json` object.
|
||||
|
||||
The cluster that is creating the backup will have the Backup resource present and will be able to manage the backup
|
||||
before the backup completes.
|
||||
|
||||
If the Backup resource is removed (e.g. Velero is uninstalled) before a backup completes and writes its
|
||||
`velero-backup.json` object, the other objects in the object store for the backup will be effectively orphaned. This
|
||||
can currently happen but the current window is much smaller.
|
||||
|
||||
### `<backup-name>-itemsnapshots.json.gz`
|
||||
The itemsnapshots file is similar to the existing `<backup-name>-itemsnapshots.json.gz` Each snapshot taken via
|
||||
SnapshotItemAction will have a JSON record in the file. Exact format TBD.
|
||||
|
||||
## CSI snapshots
|
||||
|
||||
For systems such as EBS, a snapshot is not available until the storage system has transferred the snapshot to
|
||||
stable storage. CSI snapshots expose the _readyToUse_ state that, in the case of EBS, indicates that the snapshot
|
||||
has been transferred to durable storage and is ready to be used. The CSI BackupItemProgress.Progress method will
|
||||
poll that field and when completed, return completion.
|
||||
|
||||
## vSphere plug-in
|
||||
|
||||
The vSphere Plug-in for Velero uploads snapshots to S3 in the background. This is also a BackupItemAction plug-in,
|
||||
it will check the status of the Upload records for the snapshot and return progress.
|
||||
|
||||
## Backup workflow changes
|
||||
|
||||
The backup workflow remains the same until we get to the point where the `velero-backup.json` object is written.
|
||||
At this point, we will queue the backup to a finalization go-routine. The next backup may then begin. The finalization
|
||||
routine will run across all of the volume snapshots and call the _UploadProgress_ method on each of them. It will
|
||||
then run across all items and call _BackupItemProgress.Progress_ for any that match with a BackupItemProgress.
|
||||
|
||||
If all snapshots and backup items have finished uploading (either successfully or failed), the backup will be completed
|
||||
and the backup will move to the appropriate terminal phase and upload the `velero-backup.json` object to the object store
|
||||
and the backup will be complete.
|
||||
|
||||
If any of the snapshots or backup items are still being processed, the phase of the backup will be set to the appropriate
|
||||
phase (_Uploading_ or _UploadingPartialFailure_). In the event of any of the upload progress checks return an error, the
|
||||
phase will move to _UploadingPartialFailure_. The backup will then be requeued and will be rechecked again after some
|
||||
time has passed.
|
||||
|
||||
## Restart workflow
|
||||
On restart, the Velero server will scan all Backup resources. Any Backup resources which are in the _InProgress_ phase
|
||||
will be moved to the _Failed_ phase. Any Backup resources in the _Oploading_ or _OploadingPartialFailure_ phase will
|
||||
be treated as if they have been requeued and progress checked and the backup will be requeued or moved to a terminal
|
||||
phase as appropriate.
|
||||
|
||||
# Implementation tasks
|
||||
|
||||
VolumeSnapshotter new plugin APIs
|
||||
BackupItemProgress new plugin interface
|
||||
New backup phases
|
||||
Defer uploading `velero-backup.json`
|
||||
AWS EBS plug-in UploadProgress implementation
|
||||
Upload monitoring
|
||||
Implementation of `<backup-name>-itemsnapshots.json.gz` file
|
||||
Restart logic
|
||||
Change in reconciliation logic to ignore backups that have not completed
|
||||
CSI plug-in BackupItemProgress implementation
|
||||
vSphere plug-in BackupItemProgress implementation (vSphere plug-in team)
|
||||
|
||||
# Future Fragile/Durable snapshot tracking
|
||||
Futures are here for reference, they may change radically when actually implemented.
|
||||
|
||||
Some storage systems have the ability to provide different levels of protection for snapshots. These are termed "Fragile"
|
||||
and "Durable". Currently, Velero expects snapshots to be Durable (they should be able to survive the destruction of the
|
||||
cluster and the storage it is using). In the future we would like the ability to take advantage of snapshots that are
|
||||
Fragile. For example, vSphere snapshots are Fragile (they reside in the same datastore as the virtual disk). The Velero
|
||||
Plug-in for vSphere uses a vSphere local/fragile snapshot to get a consistent snapshot, then uploads the data to S3 to
|
||||
make it Durable. In the current design, upload progress will not be complete until the snapshot is ready to use and
|
||||
Durable. It is possible, however, to restore data from a vSphere snapshot before it has been made Durable, and this is a
|
||||
capability we'd like to expose in the future. Other storage systems implement this functionality as well. We will be moving
|
||||
the control of the data movement from the vSphere plug-in into Velero.
|
||||
|
||||
Some storage system, such as EBS, are only capable of creating Durable snapshots. There is no usable intermediate Fragile stage.
|
||||
|
||||
For a Velero backup, users should be able to specify whether they want a Durable backup or a Fragile backup (Fragile backups
|
||||
may consume less resources, be quicker to restore from and are suitable for things like backing up a cluster before upgrading
|
||||
software). We can introduce three snapshot states - Creating, Fragile and Durable. A snapshot would be created with a
|
||||
desired state, Fragile or Durable. When the snapshot reaches the desired or higher state (e.g. request was for Fragile but
|
||||
snapshot went to Durable as on EBS), then the snapshot would be completed.
|
||||
@@ -50,82 +50,54 @@ we still have a reference to the additional items (`GroupResource` and
|
||||
namespaced name), as well as a reference to the `RestoreItemAction`
|
||||
plugin which required it.
|
||||
|
||||
At this point, if the `RestoreItemActionExecuteOutput`
|
||||
`WaitForAdditionalItems` field is set to `true` we need to call a func
|
||||
similar to `crdAvailable` which we will call `itemsAvailable`
|
||||
At this point, if the `RestoreItemActionExecuteOutput` includes a
|
||||
non-nil `AdditionalItemsReadyFunc` we need to call a func similar to
|
||||
`crdAvailable` which we will call `itemsAvailable`
|
||||
https://github.com/vmware-tanzu/velero/blob/main/pkg/restore/restore.go#L623
|
||||
This func should also be defined within restore.go
|
||||
|
||||
Instead of the one minute CRD timeout, we'll use a timeout specific to
|
||||
waiting for additional items. There will be a new field added to
|
||||
serverConfig, `additionalItemsReadyTimeout`, with a
|
||||
`defaultAdditionalItemsReadyTimeout` const set to 10 minutes. In
|
||||
addition, each plugin will be able to define an override for the
|
||||
global server-level value, which will be added as another optional
|
||||
field in the `RestoreItemActionExecuteOutput` struct. Instead of the
|
||||
`IsUnstructuredCRDReady` call, we'll call `AreAdditionalItemsReady` on
|
||||
the plugin, passing in the same `AdditionalItems` slice as an argument
|
||||
(with items which failed to restore filtered out). If this func
|
||||
returns an error, then `itemsAvailable` will propagate the error, and
|
||||
`restoreItem` will handle it the same way it handles an error return
|
||||
on restoring an additional item. If the timeout is reached without
|
||||
ready returning true, velero will continue on to attempt restore of
|
||||
the current item.
|
||||
|
||||
### `RestoreItemAction` plugin interface changes
|
||||
|
||||
In order to implement the `AreAdditionalItemsReady` plugin func, a new
|
||||
function will be added to the `RestoreItemAction` interface.
|
||||
```
|
||||
type RestoreItemAction interface {
|
||||
// AppliesTo returns information about which resources this action should be invoked for.
|
||||
// A RestoreItemAction's Execute function will only be invoked on items that match the returned
|
||||
// selector. A zero-valued ResourceSelector matches all resources.
|
||||
AppliesTo() (ResourceSelector, error)
|
||||
|
||||
// Execute allows the ItemAction to perform arbitrary logic with the item being restored,
|
||||
// including mutating the item itself prior to restore. The item (unmodified or modified)
|
||||
// should be returned, along with an optional slice of ResourceIdentifiers specifying additional
|
||||
// related items that should be restored, a warning (which will be logged but will not prevent
|
||||
// the item from being restored) or error (which will be logged and will prevent the item
|
||||
// from being restored) if applicable.
|
||||
Execute(input *RestoreItemActionExecuteInput) (*RestoreItemActionExecuteOutput, error)
|
||||
|
||||
// AreAdditionalItemsReady allows the ItemAction to communicate whether the passed-in
|
||||
// slice of AdditionalItems (previously returned by Execute())
|
||||
// are ready. Returns true if all items are ready, and false
|
||||
// otherwise. The second return value is an error string if an
|
||||
// error occurred.
|
||||
AreAdditionalItemsReady(restore *api.Restore, AdditionalItems []ResourceIdentifier) (bool, string)
|
||||
}
|
||||
```
|
||||
`defaultAdditionalItemsReadyTimeout` const set to 10 minutes. In addition,
|
||||
each plugin will be able to define an override for the global
|
||||
server-level value, which will be added as another optional field in
|
||||
the `RestoreItemActionExecuteOutput` struct. Instead of the
|
||||
`IsUnstructuredCRDReady` call, we'll call the returned
|
||||
`AdditionalItemsReadyFunc` passing in the same `AdditionalItems` slice
|
||||
as an argument (with items which failed to restore filtered out). If
|
||||
this func returns an error, then `itemsAvailable` will
|
||||
propagate the error, and `restoreItem` will handle it the same way it
|
||||
handles an error return on restoring an additional item. If the
|
||||
timeout is reached without ready returning true, velero will continue
|
||||
on to attempt restore of the current item.
|
||||
|
||||
### `RestoreItemActionExecuteOutput` changes
|
||||
|
||||
Two new fields will be added to `RestoreItemActionExecuteOutput`, both
|
||||
optional. `AdditionalItemsReadyTimeout`, if non-zero, will override
|
||||
`serverConfig.additionalItemsReadyTimeout`. If
|
||||
`WaitForAdditionalItems` is true, then `restoreItem` will call
|
||||
optional. `AdditionalItemsReadyTimeout`, if specified, will override
|
||||
`serverConfig.additionalItemsReadyTimeout`. If the new func field
|
||||
`AdditionalItemsReadyFunc` is non-nil, then `restoreItem` will call
|
||||
`itemsAvailable` which will invoke the plugin func
|
||||
`AreAdditionalItemsReady` and wait until the func returns true or the
|
||||
timeout is reached. If `WaitForAdditionalItems` is false (the default
|
||||
`AdditionalItemsReadyFunc` and wait until the func returns true or the
|
||||
timeout is reached. If `AdditionalItemsReadyFunc` is nil (the default
|
||||
case), then current velero behavior will be followed. Existing plugins
|
||||
which do not need to signal to wait for `AdditionalItems` won't need
|
||||
to change their `Execute()` functions.
|
||||
|
||||
In addition, a new func, `WithItemsWait()` will
|
||||
In addition, a new func, `WithItemsWait(readyFunc *func)` will
|
||||
be added to `RestoreItemActionExecuteOutput` similar to
|
||||
`WithoutRestore()` which will set `WaitForAdditionalItems` to
|
||||
true. This will allow a plugin to include waiting for
|
||||
`WithoutRestore()` which will set `AdditionalItemsReadyFunc` to
|
||||
`readyfunc`. This will allow a plugin to include waiting for
|
||||
AdditionalItems like this:
|
||||
```
|
||||
func AreAdditionalItemsReady (restore *api.Restore, additionalItems []ResourceIdentifier) (bool, string) {
|
||||
func AreItemsReady (restore *api.Restore, additionalItems []ResourceIdentifier) (bool, error) {
|
||||
...
|
||||
return true, ""
|
||||
return true, nil
|
||||
}
|
||||
func (p *RestorePlugin) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
...
|
||||
return velero.NewRestoreItemActionExecuteOutput(input.Item).WithItemsWait(), nil
|
||||
return velero.NewRestoreItemActionExecuteOutput(input.Item).WithItemsWait(AreItemsReady), nil
|
||||
}
|
||||
|
||||
```
|
||||
@@ -146,6 +118,68 @@ type RestoreItemActionExecuteOutput struct {
|
||||
// value is true, AdditionalItems will be ignored.
|
||||
SkipRestore bool
|
||||
|
||||
// AdditionalItemsReadyFunc is a func which returns true if
|
||||
// the additionalItems passed into the func are
|
||||
// ready/available. A nil value for this func means that
|
||||
// velero will not wait for the items to be ready before
|
||||
// attempting to restore the current item.
|
||||
AdditionalItemsReadyFunc func(restore *api.Restore, []ResourceIdentifier) (bool, error)
|
||||
|
||||
// AdditionalItemsReadyTimeout will override serverConfig.additionalItemsReadyTimeout
|
||||
// if specified. This value specifies how long velero will wait
|
||||
// for additional items to be ready before moving on.
|
||||
AdditionalItemsReadyTimeout *time.Duration
|
||||
}
|
||||
|
||||
// WithoutRestore returns SkipRestore for RestoreItemActionExecuteOutput
|
||||
func (r *RestoreItemActionExecuteOutput) WithItemsWait(
|
||||
readyFunc func(*api.Restore, []ResourceIdentifier)
|
||||
) *RestoreItemActionExecuteOutput {
|
||||
r.AdditionalItemsReadyFunc = readyFunc
|
||||
return r
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Earlier iteration (no longer the current implementation plan)
|
||||
|
||||
What follows is the first iteration of the design. Everything from
|
||||
here is superseded by the content above. The options below require
|
||||
either breaking backwards compatibility or dealing with runtime
|
||||
casting and optional interfaces. Adding the func pointer to
|
||||
`RestoreItemActionExecuteOutput` resolves the problem without
|
||||
requiring either.
|
||||
|
||||
#### `RestoreItemActionExecuteOutput` changes
|
||||
|
||||
A new boolean field will be added to
|
||||
`RestoreItemActionExecuteOutput`. If `WaitForAdditionalItems` is true,
|
||||
then `restoreItem` will call `itemsAvailable` which will invoke the
|
||||
plugin func `AreAdditionalItemsReady` and wait until the func returns
|
||||
true or the timeout is reached. If `WaitForAdditionalItems` is false
|
||||
(the default case), then current velero behavior will be
|
||||
followed. Existing plugins which do not need to signal to wait for
|
||||
`AdditionalItems` won't need to change their `Execute()` functions.
|
||||
|
||||
In addition, a new func, `WithItemsWait()` will be added to
|
||||
`RestoreItemActionExecuteOutput` similar to `WithoutRestore()` which
|
||||
will set the `WaitForAdditionalItems` bool to `true`.
|
||||
|
||||
```
|
||||
// RestoreItemActionExecuteOutput contains the output variables for the ItemAction's Execution function.
|
||||
type RestoreItemActionExecuteOutput struct {
|
||||
// UpdatedItem is the item being restored mutated by ItemAction.
|
||||
UpdatedItem runtime.Unstructured
|
||||
|
||||
// AdditionalItems is a list of additional related items that should
|
||||
// be restored.
|
||||
AdditionalItems []ResourceIdentifier
|
||||
|
||||
// SkipRestore tells velero to stop executing further actions
|
||||
// on this item, and skip the restore step. When this field's
|
||||
// value is true, AdditionalItems will be ignored.
|
||||
SkipRestore bool
|
||||
|
||||
// WaitForAdditionalItems determines whether velero will wait
|
||||
// until AreAdditionalItemsReady returns true before restoring
|
||||
// this item. If this field's value is true, then after restoring
|
||||
@@ -153,55 +187,65 @@ type RestoreItemActionExecuteOutput struct {
|
||||
// until AreAdditionalItemsReady returns true or the timeout is
|
||||
// reached. Otherwise, AreAdditionalItemsReady is not called.
|
||||
WaitForAdditionalItems bool
|
||||
|
||||
// AdditionalItemsReadyTimeout will override serverConfig.additionalItemsReadyTimeout
|
||||
// if specified. This value specifies how long velero will wait
|
||||
// for additional items to be ready before moving on.
|
||||
AdditionalItemsReadyTimeout time.Duration
|
||||
}
|
||||
|
||||
// WithItemsWait returns RestoreItemActionExecuteOutput with WaitForAdditionalItems set to true.
|
||||
func (r *RestoreItemActionExecuteOutput) WithItemsWait()
|
||||
) *RestoreItemActionExecuteOutput {
|
||||
r.WaitForAdditionalItems = true
|
||||
return r
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## New design iteration (Feb 2021)
|
||||
|
||||
In starting the implementation based on the originally approved
|
||||
design, I've run into an unexpected snag. When adding the wait func
|
||||
pointer to the `RestoreItemActionExecuteOutput` struct, I had
|
||||
forgotten about the protocol buffer message format that's used for
|
||||
passing args to the plugin methods. Funcs are predefined RPC calls
|
||||
with autogenerated go code, so we can't just pass a regular golang
|
||||
func pointer in the struct. I've modified the above design to instead
|
||||
use an explicit `AreAdditionalItemsReady` func. Since this will break
|
||||
backwards compatibility with current `RestoreItemAction` plugins,
|
||||
implementation of this feature should wait until Velero plugin
|
||||
versioning, as described in
|
||||
https://github.com/vmware-tanzu/velero/issues/3285 is
|
||||
implemented. With plugin versioning in place, existing (non-versioned
|
||||
or 1.0-versioned) `RestoreItemAction` plugins which do not define
|
||||
`AreAdditionalItemsReady` would be able to coexist with a
|
||||
to-be-implemented `RestoreItemAction` plugin version 2.0 (or 1.1,
|
||||
etc.) which defines this new interface method. Without plugin
|
||||
versioning, implementing this feature would break all existing plugins
|
||||
until they define `AreAdditionalItemsReady`.
|
||||
|
||||
Also note that when moving to the new plugin version, the vast
|
||||
majority of plugins will probably not need to wait for additional
|
||||
items. All they will need to do to react to this plugin interface
|
||||
change would be to define the following in the plugin:
|
||||
|
||||
```
|
||||
func AreAdditionalItemsReady (restore *api.Restore, additionalItems []ResourceIdentifier) (bool, string) {
|
||||
return true, ""
|
||||
}
|
||||
```
|
||||
|
||||
As long as they never set `WaitForAdditionalItems` to true, this
|
||||
function won't be called anyway, but if it is called, there will be no
|
||||
waiting, since it will always return true.
|
||||
#### `RestoreItemAction` plugin interface changes
|
||||
|
||||
In order to implement the `AreAdditionalItemsReady` plugin func, there
|
||||
are two different approaches we could take.
|
||||
|
||||
The first would be to simply add another entry to the
|
||||
`RestoreItemAction` interface:
|
||||
```
|
||||
type RestoreItemAction interface {
|
||||
// AppliesTo returns information about which resources this action should be invoked for.
|
||||
// A RestoreItemAction's Execute function will only be invoked on items that match the returned
|
||||
// selector. A zero-valued ResourceSelector matches all resources.
|
||||
AppliesTo() (ResourceSelector, error)
|
||||
|
||||
// Execute allows the ItemAction to perform arbitrary logic with the item being restored,
|
||||
// including mutating the item itself prior to restore. The item (unmodified or modified)
|
||||
// should be returned, along with an optional slice of ResourceIdentifiers specifying additional
|
||||
// related items that should be restored, a warning (which will be logged but will not prevent
|
||||
// the item from being restored) or error (which will be logged and will prevent the item
|
||||
// from being restored) if applicable.
|
||||
Execute(input *RestoreItemActionExecuteInput) (*RestoreItemActionExecuteOutput, error)
|
||||
|
||||
// AreAdditionalItemsReady allows the ItemAction to communicate whether the passed-in
|
||||
// slice of AdditionalItems (previously returned by Execute())
|
||||
// are ready. Returns true if all items are ready, and false otherwise
|
||||
AreAdditionalItemsReady(restore *api.Restore, AdditionalItems []ResourceIdentifier) (bool, error)
|
||||
}
|
||||
```
|
||||
|
||||
The downside of this approach is that it is not backwards compatible,
|
||||
and every `RestoreItemAction` plugin will have to implement the new
|
||||
func, simply to return `true` in most cases, since the plugin will
|
||||
either never return `AdditionalItems` from Execute or not have any
|
||||
special readiness requirements.
|
||||
|
||||
The alternative to this would be to define an additional interface for
|
||||
the optional func, leaving the `RestoreItemAction` interface alone.
|
||||
```
|
||||
type RestoreItemActionReadyCheck interface {
|
||||
// AreAdditionalItemsReady allows the ItemAction to communicate whether the passed-in
|
||||
// slice of AdditionalItems (previously returned by Execute())
|
||||
// are ready. Returns true if all items are ready, and false otherwise
|
||||
AreAdditionalItemsReady(restore *api.Restore, AdditionalItems []ResourceIdentifier) (bool, error)
|
||||
}
|
||||
|
||||
```
|
||||
In this case, existing plugins which do not need this functionality
|
||||
can remain as-is, while plugins which want to make use of this
|
||||
functionality will just need to implement the optional func. With the
|
||||
optional interface approach, `itemsAvailable` will only wait if the
|
||||
plugin can be type-asserted to the new interface:
|
||||
```
|
||||
if actionWithReadyCheck, ok := action.(RestoreItemActionReadyCheck); ok {
|
||||
// wait for ready/timeout
|
||||
} else {
|
||||
return true, nil
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
52
go.mod
@@ -1,47 +1,51 @@
|
||||
module github.com/vmware-tanzu/velero
|
||||
|
||||
go 1.16
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go v42.0.0+incompatible
|
||||
github.com/Azure/go-autorest/autorest v0.11.21
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8
|
||||
github.com/Azure/go-autorest/autorest v0.9.6
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0
|
||||
github.com/Azure/go-autorest/autorest/validation v0.2.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.28.2
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/docker/spdystream v0.0.0-20170912183627-bc6354cbbc29 // indirect
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible
|
||||
github.com/fatih/color v1.10.0
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gofrs/uuid v3.2.0+incompatible
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/golang/protobuf v1.4.2
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/hashicorp/go-hclog v0.12.0
|
||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd
|
||||
github.com/hashicorp/go-plugin v0.0.0-20190610192547-a1bc61569a26
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/kubernetes-csi/external-snapshotter/client/v4 v4.0.0
|
||||
github.com/onsi/ginkgo v1.16.4
|
||||
github.com/onsi/gomega v1.16.0
|
||||
github.com/onsi/gomega v1.10.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/prometheus/client_golang v1.7.1
|
||||
github.com/robfig/cron v1.1.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/afero v1.6.0
|
||||
github.com/spf13/cobra v1.2.1
|
||||
github.com/sirupsen/logrus v1.6.0
|
||||
github.com/spf13/afero v1.2.2
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/vmware-tanzu/crash-diagnostics v0.3.7
|
||||
golang.org/x/mod v0.4.2
|
||||
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023
|
||||
google.golang.org/grpc v1.40.0
|
||||
k8s.io/api v0.22.2
|
||||
k8s.io/apiextensions-apiserver v0.22.2
|
||||
k8s.io/apimachinery v0.22.2
|
||||
k8s.io/cli-runtime v0.22.2
|
||||
k8s.io/client-go v0.22.2
|
||||
github.com/stretchr/testify v1.5.1
|
||||
golang.org/x/mod v0.3.0
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
|
||||
google.golang.org/genproto v0.0.0-20200731012542-8145dea6a485 // indirect
|
||||
google.golang.org/grpc v1.31.0
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
k8s.io/api v0.19.12
|
||||
k8s.io/apiextensions-apiserver v0.19.12
|
||||
k8s.io/apimachinery v0.19.12
|
||||
k8s.io/cli-runtime v0.19.12
|
||||
k8s.io/client-go v0.19.12
|
||||
k8s.io/klog v1.0.0
|
||||
k8s.io/klog/v2 v2.3.0 // indirect
|
||||
k8s.io/kube-aggregator v0.19.12
|
||||
sigs.k8s.io/cluster-api v1.0.0
|
||||
sigs.k8s.io/controller-runtime v0.10.2
|
||||
k8s.io/utils v0.0.0-20201005171033-6301aaf42dc7 // indirect
|
||||
sigs.k8s.io/cluster-api v0.3.11-0.20210106212952-b6c1b5b3db3d
|
||||
sigs.k8s.io/controller-runtime v0.7.1-0.20201215171748-096b2e07c091
|
||||
)
|
||||
|
||||
replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM golang:1.16
|
||||
FROM golang:1.15
|
||||
|
||||
ARG GOPROXY
|
||||
|
||||
|
||||
@@ -89,31 +89,18 @@ fi
|
||||
# Since we're past the validation of the VELERO_VERSION, parse the version's individual components.
|
||||
eval $(go run $DIR/chk_version.go)
|
||||
|
||||
|
||||
printf "To clarify, you've provided a version string of $VELERO_VERSION.\n"
|
||||
printf "Based on this, the following assumptions have been made: \n"
|
||||
|
||||
# $VELERO_PATCH gets populated by the chk_version.go scrip that parses and verifies the given version format
|
||||
# If we've got a patch release, we assume the tag is on release branch.
|
||||
if [[ "$VELERO_PATCH" != 0 ]]; then
|
||||
printf "*\t This is a patch release.\n"
|
||||
ON_RELEASE_BRANCH=TRUE
|
||||
fi
|
||||
[[ "$VELERO_PATCH" != 0 ]] && printf "*\t This is a patch release.\n"
|
||||
|
||||
# $VELERO_PRERELEASE gets populated by the chk_version.go script that parses and verifies the given version format
|
||||
# If we've got a GA release, we assume the tag is on release branch.
|
||||
# $VELERO_PRERELEASE gets populated by the chk_version.go script that parses and verifies the given version format
|
||||
# -n is "string is non-empty"
|
||||
[[ -n $VELERO_PRERELEASE ]] && printf "*\t This is a pre-release.\n"
|
||||
|
||||
# -z is "string is empty"
|
||||
if [[ -z $VELERO_PRERELEASE ]]; then
|
||||
printf "*\t This is a GA release.\n"
|
||||
ON_RELEASE_BRANCH=TRUE
|
||||
fi
|
||||
|
||||
if [[ "$ON_RELEASE_BRANCH" == "TRUE" ]]; then
|
||||
release_branch_name=release-$VELERO_MAJOR.$VELERO_MINOR
|
||||
printf "*\t The commit to tag is on branch: %s. Please make sure this branch has been created.\n" $release_branch_name
|
||||
fi
|
||||
[[ -z $VELERO_PRERELEASE ]] && printf "*\t This is a GA release.\n"
|
||||
|
||||
if [[ $publish == "TRUE" ]]; then
|
||||
echo "If this is all correct, press enter/return to proceed to TAG THE RELEASE and UPLOAD THE TAG TO GITHUB."
|
||||
@@ -130,29 +117,55 @@ echo "Alright, let's go."
|
||||
echo "Pulling down all git tags and branches before doing any work."
|
||||
git fetch "$remote" --tags
|
||||
|
||||
if [[ -n $release_branch_name ]]; then
|
||||
# Tag on release branch
|
||||
# $VELERO_PATCH gets populated by the chk_version.go scrip that parses and verifies the given version format
|
||||
# If we've got a patch release, we'll need to create a release branch for it.
|
||||
if [[ "$VELERO_PATCH" > 0 ]]; then
|
||||
release_branch_name=release-$VELERO_MAJOR.$VELERO_MINOR
|
||||
remote_release_branch_name="$remote/$release_branch_name"
|
||||
|
||||
# Determine whether the local and remote release branches already exist
|
||||
local_branch=$(git branch | grep "$release_branch_name")
|
||||
remote_branch=$(git branch -r | grep "$remote_release_branch_name")
|
||||
if [[ -z $remote_branch ]]; then
|
||||
echo "The branch $remote_release_branch_name must be created before you tag the release."
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z $local_branch ]]; then
|
||||
|
||||
if [[ -n $remote_branch ]]; then
|
||||
if [[ -z $local_branch ]]; then
|
||||
# Remote branch exists, but does not exist locally. Checkout and track the remote branch.
|
||||
git checkout --track "$remote_release_branch_name"
|
||||
else
|
||||
else
|
||||
# Checkout the local release branch and ensure it is up to date with the remote
|
||||
git checkout "$release_branch_name"
|
||||
git pull --set-upstream "$remote" "$release_branch_name"
|
||||
fi
|
||||
else
|
||||
if [[ -z $local_branch ]]; then
|
||||
# Neither the remote or local release branch exists, create it
|
||||
git checkout -b $release_branch_name
|
||||
else
|
||||
# The local branch exists so check it out.
|
||||
git checkout $release_branch_name
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Now you'll need to cherry-pick any relevant git commits into this release branch."
|
||||
echo "Either pause this script with ctrl-z, or open a new terminal window and do the cherry-picking."
|
||||
if [[ $publish == "TRUE" ]]; then
|
||||
read -p "Press enter when you're done cherry-picking. THIS WILL MAKE A TAG PUSH THE BRANCH TO $remote"
|
||||
else
|
||||
read -p "Press enter when you're done cherry-picking."
|
||||
fi
|
||||
|
||||
# TODO can/should we add a way to review the cherry-picked commits before the push?
|
||||
|
||||
if [[ $publish == "TRUE" ]]; then
|
||||
echo "Pushing $release_branch_name to \"$remote\" remote"
|
||||
git push --set-upstream "$remote" $release_branch_name
|
||||
fi
|
||||
|
||||
tag_and_push
|
||||
else
|
||||
echo "Checking out $remote/main."
|
||||
git checkout "$remote"/main
|
||||
|
||||
tag_and_push
|
||||
fi
|
||||
|
||||
|
||||
@@ -38,10 +38,5 @@ if [[ -n "${GOFLAGS:-}" ]]; then
|
||||
echo "GOFLAGS: ${GOFLAGS}"
|
||||
fi
|
||||
|
||||
# After bumping up "sigs.k8s.io/controller-runtime" to v0.10.2, get the error "panic: mkdir /.cache/kubebuilder-envtest: permission denied"
|
||||
# when running this script with "make test" command. This is caused by that "make test" runs inside a container with user and group specified,
|
||||
# but the user and group don't exist inside the container, when the code(https://github.com/kubernetes-sigs/controller-runtime/blob/v0.10.2/pkg/internal/testing/addr/manager.go#L44)
|
||||
# tries to get the cache directory, it gets the directory "/" and then get the permission error when trying to create directory under "/".
|
||||
# Specifying the cache directory by environment variable "XDG_CACHE_HOME" to workaround it
|
||||
XDG_CACHE_HOME=/tmp/ go test -installsuffix "static" -short -timeout 60s "${TARGETS[@]}"
|
||||
go test -installsuffix "static" -short -timeout 60s "${TARGETS[@]}"
|
||||
echo "Success!"
|
||||
|
||||
@@ -29,7 +29,6 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/archive"
|
||||
"github.com/vmware-tanzu/velero/pkg/discovery"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
deleteactionitemv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/deleteitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/collections"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
|
||||
)
|
||||
@@ -38,7 +37,7 @@ import (
|
||||
type Context struct {
|
||||
Backup *velerov1api.Backup
|
||||
BackupReader io.Reader
|
||||
Actions []deleteactionitemv2.DeleteItemAction
|
||||
Actions []velero.DeleteItemAction
|
||||
Filesystem filesystem.Interface
|
||||
Log logrus.FieldLogger
|
||||
DiscoveryHelper discovery.Helper
|
||||
@@ -69,10 +68,6 @@ func InvokeDeleteActions(ctx *Context) error {
|
||||
ctx.Log.Debugf("Downloaded and extracted the backup file to: %s", dir)
|
||||
|
||||
backupResources, err := archive.NewParser(ctx.Log, ctx.Filesystem).Parse(dir)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error parsing backup %q", dir)
|
||||
}
|
||||
|
||||
processdResources := sets.NewString()
|
||||
|
||||
ctx.Log.Debugf("Trying to reconcile resource names with Kube API server.")
|
||||
@@ -164,7 +159,7 @@ func (ctx *Context) getApplicableActions(groupResource schema.GroupResource, nam
|
||||
|
||||
// resolvedActions are DeleteItemActions decorated with resource/namespace include/exclude collections, as well as label selectors for easy comparison.
|
||||
type resolvedAction struct {
|
||||
deleteactionitemv2.DeleteItemAction
|
||||
velero.DeleteItemAction
|
||||
|
||||
resourceIncludesExcludes *collections.IncludesExcludes
|
||||
namespaceIncludesExcludes *collections.IncludesExcludes
|
||||
@@ -172,7 +167,7 @@ type resolvedAction struct {
|
||||
}
|
||||
|
||||
// resolveActions resolves the AppliesTo ResourceSelectors of DeleteItemActions plugins against the Kubernetes discovery API for fully-qualified names.
|
||||
func resolveActions(actions []deleteactionitemv2.DeleteItemAction, helper discovery.Helper) ([]resolvedAction, error) {
|
||||
func resolveActions(actions []velero.DeleteItemAction, helper discovery.Helper) ([]resolvedAction, error) {
|
||||
var resolved []resolvedAction
|
||||
|
||||
for _, action := range actions {
|
||||
|
||||
@@ -53,7 +53,7 @@ func IsReadyToValidate(bslValidationFrequency *metav1.Duration, lastValidationTi
|
||||
}
|
||||
|
||||
if validationFrequency < 0 {
|
||||
log.Debugf("Validation period must be non-negative, changing from %d to %d", validationFrequency, serverValidationFrequency)
|
||||
log.Debugf("Validation period must be non-negative, changing from %d to %d", validationFrequency, validationFrequency)
|
||||
validationFrequency = serverValidationFrequency
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,8 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type Metadata struct {
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
// BackupSpec defines the specification for a Velero backup.
|
||||
type BackupSpec struct {
|
||||
// +optional
|
||||
Metadata `json:"metadata,omitempty"`
|
||||
// IncludedNamespaces is a slice of namespace names to include objects
|
||||
// from. If empty, all namespaces are included.
|
||||
// +optional
|
||||
@@ -213,17 +207,6 @@ const (
|
||||
// BackupPhaseInProgress means the backup is currently executing.
|
||||
BackupPhaseInProgress BackupPhase = "InProgress"
|
||||
|
||||
// BackupPhaseUploading means the backups of Kubernetes resources
|
||||
// and creation of snapshots was successful and snapshot data
|
||||
// is currently uploading. The backup is not usable yet.
|
||||
BackupPhaseUploading BackupPhase = "Uploading"
|
||||
|
||||
// BackupPhaseUploadingPartialFailure means the backup of Kubernetes
|
||||
// resources and creation of snapshots partially failed (final phase
|
||||
// will be PartiallyFailed) and snapshot data is currently uploading.
|
||||
// The backup is not usable yet.
|
||||
BackupPhaseUploadingPartialFailure BackupPhase = "UploadingPartialFailure"
|
||||
|
||||
// BackupPhaseCompleted means the backup has run successfully without
|
||||
// errors.
|
||||
BackupPhaseCompleted BackupPhase = "Completed"
|
||||
|
||||
@@ -205,7 +205,6 @@ func (in *BackupResourceHookSpec) DeepCopy() *BackupResourceHookSpec {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *BackupSpec) DeepCopyInto(out *BackupSpec) {
|
||||
*out = *in
|
||||
in.Metadata.DeepCopyInto(&out.Metadata)
|
||||
if in.IncludedNamespaces != nil {
|
||||
in, out := &in.IncludedNamespaces, &out.IncludedNamespaces
|
||||
*out = make([]string, len(*in))
|
||||
@@ -716,29 +715,6 @@ func (in *InitRestoreHook) DeepCopy() *InitRestoreHook {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Metadata) DeepCopyInto(out *Metadata) {
|
||||
*out = *in
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Metadata.
|
||||
func (in *Metadata) DeepCopy() *Metadata {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Metadata)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ObjectStorageLocation) DeepCopyInto(out *ObjectStorageLocation) {
|
||||
*out = *in
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright the Velero contributors.
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -44,8 +44,7 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/discovery"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
||||
backupitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2"
|
||||
volumesnapshotterv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v2"
|
||||
"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/util/collections"
|
||||
@@ -62,8 +61,7 @@ const BackupFormatVersion = "1.1.0"
|
||||
type Backupper interface {
|
||||
// Backup takes a backup using the specification in the velerov1api.Backup and writes backup and log data
|
||||
// to the given writers.
|
||||
Backup(logger logrus.FieldLogger, backup *Request, backupFile io.Writer,
|
||||
actions []backupitemactionv2.BackupItemAction, volumeSnapshotterGetter VolumeSnapshotterGetter) error
|
||||
Backup(logger logrus.FieldLogger, backup *Request, backupFile io.Writer, actions []velero.BackupItemAction, volumeSnapshotterGetter VolumeSnapshotterGetter) error
|
||||
}
|
||||
|
||||
// kubernetesBackupper implements Backupper.
|
||||
@@ -75,11 +73,10 @@ type kubernetesBackupper struct {
|
||||
resticBackupperFactory restic.BackupperFactory
|
||||
resticTimeout time.Duration
|
||||
defaultVolumesToRestic bool
|
||||
clientPageSize int
|
||||
}
|
||||
|
||||
type resolvedAction struct {
|
||||
backupitemactionv2.BackupItemAction
|
||||
velero.BackupItemAction
|
||||
|
||||
resourceIncludesExcludes *collections.IncludesExcludes
|
||||
namespaceIncludesExcludes *collections.IncludesExcludes
|
||||
@@ -109,7 +106,6 @@ func NewKubernetesBackupper(
|
||||
resticBackupperFactory restic.BackupperFactory,
|
||||
resticTimeout time.Duration,
|
||||
defaultVolumesToRestic bool,
|
||||
clientPageSize int,
|
||||
) (Backupper, error) {
|
||||
return &kubernetesBackupper{
|
||||
backupClient: backupClient,
|
||||
@@ -119,11 +115,10 @@ func NewKubernetesBackupper(
|
||||
resticBackupperFactory: resticBackupperFactory,
|
||||
resticTimeout: resticTimeout,
|
||||
defaultVolumesToRestic: defaultVolumesToRestic,
|
||||
clientPageSize: clientPageSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func resolveActions(actions []backupitemactionv2.BackupItemAction, helper discovery.Helper) ([]resolvedAction, error) {
|
||||
func resolveActions(actions []velero.BackupItemAction, helper discovery.Helper) ([]resolvedAction, error) {
|
||||
var resolved []resolvedAction
|
||||
|
||||
for _, action := range actions {
|
||||
@@ -199,7 +194,7 @@ func getResourceHook(hookSpec velerov1api.BackupResourceHookSpec, discoveryHelpe
|
||||
}
|
||||
|
||||
type VolumeSnapshotterGetter interface {
|
||||
GetVolumeSnapshotter(name string) (volumesnapshotterv2.VolumeSnapshotter, error)
|
||||
GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error)
|
||||
}
|
||||
|
||||
// Backup backs up the items specified in the Backup, placing them in a gzip-compressed tar file
|
||||
@@ -207,8 +202,7 @@ type VolumeSnapshotterGetter interface {
|
||||
// a complete backup failure is returned. Errors that constitute partial failures (i.e. failures to
|
||||
// back up individual resources that don't prevent the backup from continuing to be processed) are logged
|
||||
// to the backup log.
|
||||
func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Request, backupFile io.Writer,
|
||||
actions []backupitemactionv2.BackupItemAction, volumeSnapshotterGetter VolumeSnapshotterGetter) error {
|
||||
func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Request, backupFile io.Writer, actions []velero.BackupItemAction, volumeSnapshotterGetter VolumeSnapshotterGetter) error {
|
||||
gzippedData := gzip.NewWriter(backupFile)
|
||||
defer gzippedData.Close()
|
||||
|
||||
@@ -278,7 +272,6 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req
|
||||
dynamicFactory: kb.dynamicFactory,
|
||||
cohabitatingResources: cohabitatingResources(),
|
||||
dir: tempDir,
|
||||
pageSize: kb.clientPageSize,
|
||||
}
|
||||
|
||||
items := collector.getAllItems()
|
||||
|
||||
@@ -47,7 +47,6 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/discovery"
|
||||
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
backupitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/test"
|
||||
testutil "github.com/vmware-tanzu/velero/pkg/test"
|
||||
@@ -971,30 +970,6 @@ func TestBackupResourceCohabitation(t *testing.T) {
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when deployments exist that are not in the cohabitating groups those are backed up along with apps/deployments",
|
||||
backup: defaultBackup().Result(),
|
||||
apiResources: []*test.APIResource{
|
||||
test.VeleroDeployments(
|
||||
builder.ForTestCR("Deployment", "foo", "bar").Result(),
|
||||
builder.ForTestCR("Deployment", "zoo", "raz").Result(),
|
||||
),
|
||||
test.Deployments(
|
||||
builder.ForDeployment("foo", "bar").Result(),
|
||||
builder.ForDeployment("zoo", "raz").Result(),
|
||||
),
|
||||
},
|
||||
want: []string{
|
||||
"resources/deployments.apps/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
"resources/deployments.velero.io/namespaces/foo/bar.json",
|
||||
"resources/deployments.velero.io/namespaces/zoo/raz.json",
|
||||
"resources/deployments.velero.io/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/deployments.velero.io/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@@ -1332,7 +1307,7 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) {
|
||||
h.addItems(t, resource)
|
||||
}
|
||||
|
||||
actions := []backupitemactionv2.BackupItemAction{}
|
||||
actions := []velero.BackupItemAction{}
|
||||
for action := range tc.actions {
|
||||
actions = append(actions, action)
|
||||
}
|
||||
@@ -1358,7 +1333,7 @@ func TestBackupWithInvalidActions(t *testing.T) {
|
||||
name string
|
||||
backup *velerov1.Backup
|
||||
apiResources []*test.APIResource
|
||||
actions []backupitemactionv2.BackupItemAction
|
||||
actions []velero.BackupItemAction
|
||||
}{
|
||||
{
|
||||
name: "action with invalid label selector results in an error",
|
||||
@@ -1374,7 +1349,7 @@ func TestBackupWithInvalidActions(t *testing.T) {
|
||||
builder.ForPersistentVolume("baz").Result(),
|
||||
),
|
||||
},
|
||||
actions: []backupitemactionv2.BackupItemAction{
|
||||
actions: []velero.BackupItemAction{
|
||||
new(recordResourcesAction).ForLabelSelector("=invalid-selector"),
|
||||
},
|
||||
},
|
||||
@@ -1392,7 +1367,7 @@ func TestBackupWithInvalidActions(t *testing.T) {
|
||||
builder.ForPersistentVolume("baz").Result(),
|
||||
),
|
||||
},
|
||||
actions: []backupitemactionv2.BackupItemAction{
|
||||
actions: []velero.BackupItemAction{
|
||||
&appliesToErrorAction{},
|
||||
},
|
||||
},
|
||||
@@ -1454,7 +1429,7 @@ func TestBackupActionModifications(t *testing.T) {
|
||||
name string
|
||||
backup *velerov1.Backup
|
||||
apiResources []*test.APIResource
|
||||
actions []backupitemactionv2.BackupItemAction
|
||||
actions []velero.BackupItemAction
|
||||
want map[string]unstructuredObject
|
||||
}{
|
||||
{
|
||||
@@ -1465,7 +1440,7 @@ func TestBackupActionModifications(t *testing.T) {
|
||||
builder.ForPod("ns-1", "pod-1").Result(),
|
||||
),
|
||||
},
|
||||
actions: []backupitemactionv2.BackupItemAction{
|
||||
actions: []velero.BackupItemAction{
|
||||
modifyingActionGetter(func(item *unstructured.Unstructured) {
|
||||
item.SetLabels(map[string]string{"updated": "true"})
|
||||
}),
|
||||
@@ -1482,7 +1457,7 @@ func TestBackupActionModifications(t *testing.T) {
|
||||
builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("should-be-removed", "true")).Result(),
|
||||
),
|
||||
},
|
||||
actions: []backupitemactionv2.BackupItemAction{
|
||||
actions: []velero.BackupItemAction{
|
||||
modifyingActionGetter(func(item *unstructured.Unstructured) {
|
||||
item.SetLabels(nil)
|
||||
}),
|
||||
@@ -1499,7 +1474,7 @@ func TestBackupActionModifications(t *testing.T) {
|
||||
builder.ForPod("ns-1", "pod-1").Result(),
|
||||
),
|
||||
},
|
||||
actions: []backupitemactionv2.BackupItemAction{
|
||||
actions: []velero.BackupItemAction{
|
||||
modifyingActionGetter(func(item *unstructured.Unstructured) {
|
||||
item.Object["spec"].(map[string]interface{})["nodeName"] = "foo"
|
||||
}),
|
||||
@@ -1517,7 +1492,7 @@ func TestBackupActionModifications(t *testing.T) {
|
||||
builder.ForPod("ns-1", "pod-1").Result(),
|
||||
),
|
||||
},
|
||||
actions: []backupitemactionv2.BackupItemAction{
|
||||
actions: []velero.BackupItemAction{
|
||||
modifyingActionGetter(func(item *unstructured.Unstructured) {
|
||||
item.SetName(item.GetName() + "-updated")
|
||||
item.SetNamespace(item.GetNamespace() + "-updated")
|
||||
@@ -1558,7 +1533,7 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
||||
name string
|
||||
backup *velerov1.Backup
|
||||
apiResources []*test.APIResource
|
||||
actions []backupitemactionv2.BackupItemAction
|
||||
actions []velero.BackupItemAction
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
@@ -1571,7 +1546,7 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
||||
builder.ForPod("ns-3", "pod-3").Result(),
|
||||
),
|
||||
},
|
||||
actions: []backupitemactionv2.BackupItemAction{
|
||||
actions: []velero.BackupItemAction{
|
||||
&pluggableAction{
|
||||
selector: velero.ResourceSelector{IncludedNamespaces: []string{"ns-1"}},
|
||||
executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
|
||||
@@ -1603,7 +1578,7 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
||||
builder.ForPod("ns-3", "pod-3").Result(),
|
||||
),
|
||||
},
|
||||
actions: []backupitemactionv2.BackupItemAction{
|
||||
actions: []velero.BackupItemAction{
|
||||
&pluggableAction{
|
||||
executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
|
||||
additionalItems := []velero.ResourceIdentifier{
|
||||
@@ -1633,7 +1608,7 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
||||
builder.ForPersistentVolume("pv-2").Result(),
|
||||
),
|
||||
},
|
||||
actions: []backupitemactionv2.BackupItemAction{
|
||||
actions: []velero.BackupItemAction{
|
||||
&pluggableAction{
|
||||
executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
|
||||
additionalItems := []velero.ResourceIdentifier{
|
||||
@@ -1666,7 +1641,7 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
||||
builder.ForPersistentVolume("pv-2").Result(),
|
||||
),
|
||||
},
|
||||
actions: []backupitemactionv2.BackupItemAction{
|
||||
actions: []velero.BackupItemAction{
|
||||
&pluggableAction{
|
||||
executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
|
||||
additionalItems := []velero.ResourceIdentifier{
|
||||
@@ -1696,7 +1671,7 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
||||
builder.ForPersistentVolume("pv-2").Result(),
|
||||
),
|
||||
},
|
||||
actions: []backupitemactionv2.BackupItemAction{
|
||||
actions: []velero.BackupItemAction{
|
||||
&pluggableAction{
|
||||
executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
|
||||
additionalItems := []velero.ResourceIdentifier{
|
||||
@@ -1727,7 +1702,7 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
||||
builder.ForPersistentVolume("pv-2").Result(),
|
||||
),
|
||||
},
|
||||
actions: []backupitemactionv2.BackupItemAction{
|
||||
actions: []velero.BackupItemAction{
|
||||
&pluggableAction{
|
||||
executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
|
||||
additionalItems := []velero.ResourceIdentifier{
|
||||
@@ -1757,7 +1732,7 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
||||
builder.ForPod("ns-3", "pod-3").Result(),
|
||||
),
|
||||
},
|
||||
actions: []backupitemactionv2.BackupItemAction{
|
||||
actions: []velero.BackupItemAction{
|
||||
&pluggableAction{
|
||||
selector: velero.ResourceSelector{IncludedNamespaces: []string{"ns-1"}},
|
||||
executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
|
||||
|
||||
@@ -39,7 +39,7 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/client"
|
||||
"github.com/vmware-tanzu/velero/pkg/discovery"
|
||||
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
||||
volumesnapshotterv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
||||
"github.com/vmware-tanzu/velero/pkg/volume"
|
||||
@@ -56,7 +56,7 @@ type itemBackupper struct {
|
||||
volumeSnapshotterGetter VolumeSnapshotterGetter
|
||||
|
||||
itemHookHandler hook.ItemHookHandler
|
||||
snapshotLocationVolumeSnapshotters map[string]volumesnapshotterv2.VolumeSnapshotter
|
||||
snapshotLocationVolumeSnapshotters map[string]velero.VolumeSnapshotter
|
||||
}
|
||||
|
||||
// backupItem backs up an individual item to tarWriter. The item may be excluded based on the
|
||||
@@ -367,8 +367,7 @@ func (ib *itemBackupper) executeActions(
|
||||
|
||||
// volumeSnapshotter instantiates and initializes a VolumeSnapshotter given a VolumeSnapshotLocation,
|
||||
// or returns an existing one if one's already been initialized for the location.
|
||||
func (ib *itemBackupper) volumeSnapshotter(snapshotLocation *velerov1api.VolumeSnapshotLocation) (
|
||||
volumesnapshotterv2.VolumeSnapshotter, error) {
|
||||
func (ib *itemBackupper) volumeSnapshotter(snapshotLocation *velerov1api.VolumeSnapshotLocation) (velero.VolumeSnapshotter, error) {
|
||||
if bs, ok := ib.snapshotLocationVolumeSnapshotters[snapshotLocation.Name]; ok {
|
||||
return bs, nil
|
||||
}
|
||||
@@ -383,7 +382,7 @@ func (ib *itemBackupper) volumeSnapshotter(snapshotLocation *velerov1api.VolumeS
|
||||
}
|
||||
|
||||
if ib.snapshotLocationVolumeSnapshotters == nil {
|
||||
ib.snapshotLocationVolumeSnapshotters = make(map[string]volumesnapshotterv2.VolumeSnapshotter)
|
||||
ib.snapshotLocationVolumeSnapshotters = make(map[string]velero.VolumeSnapshotter)
|
||||
}
|
||||
ib.snapshotLocationVolumeSnapshotters[snapshotLocation.Name] = bs
|
||||
|
||||
@@ -439,7 +438,7 @@ func (ib *itemBackupper) takePVSnapshot(obj runtime.Unstructured, log logrus.Fie
|
||||
|
||||
var (
|
||||
volumeID, location string
|
||||
volumeSnapshotter volumesnapshotterv2.VolumeSnapshotter
|
||||
volumeSnapshotter velero.VolumeSnapshotter
|
||||
)
|
||||
|
||||
for _, snapshotLocation := range ib.backupRequest.SnapshotLocations {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright the Velero contributors.
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package backup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@@ -26,13 +25,10 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/tools/pager"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/client"
|
||||
"github.com/vmware-tanzu/velero/pkg/discovery"
|
||||
@@ -49,7 +45,6 @@ type itemCollector struct {
|
||||
dynamicFactory client.DynamicFactory
|
||||
cohabitatingResources map[string]*cohabitatingResource
|
||||
dir string
|
||||
pageSize int
|
||||
}
|
||||
|
||||
type kubernetesResource struct {
|
||||
@@ -209,18 +204,16 @@ func (r *itemCollector) getResourceItems(log logrus.FieldLogger, gv schema.Group
|
||||
}
|
||||
|
||||
if cohabitator, found := r.cohabitatingResources[resource.Name]; found {
|
||||
if gv.Group == cohabitator.groupResource1.Group || gv.Group == cohabitator.groupResource2.Group {
|
||||
if cohabitator.seen {
|
||||
log.WithFields(
|
||||
logrus.Fields{
|
||||
"cohabitatingResource1": cohabitator.groupResource1.String(),
|
||||
"cohabitatingResource2": cohabitator.groupResource2.String(),
|
||||
},
|
||||
).Infof("Skipping resource because it cohabitates and we've already processed it")
|
||||
return nil, nil
|
||||
}
|
||||
cohabitator.seen = true
|
||||
if cohabitator.seen {
|
||||
log.WithFields(
|
||||
logrus.Fields{
|
||||
"cohabitatingResource1": cohabitator.groupResource1.String(),
|
||||
"cohabitatingResource2": cohabitator.groupResource2.String(),
|
||||
},
|
||||
).Infof("Skipping resource because it cohabitates and we've already processed it")
|
||||
return nil, nil
|
||||
}
|
||||
cohabitator.seen = true
|
||||
}
|
||||
|
||||
namespacesToList := getNamespacesToList(r.backupRequest.NamespaceIncludesExcludes)
|
||||
@@ -282,7 +275,6 @@ func (r *itemCollector) getResourceItems(log logrus.FieldLogger, gv schema.Group
|
||||
var items []*kubernetesResource
|
||||
|
||||
for _, namespace := range namespacesToList {
|
||||
// List items from Kubernetes API
|
||||
log = log.WithField("namespace", namespace)
|
||||
|
||||
resourceClient, err := r.dynamicFactory.ClientForGroupVersionResource(gv, resource, namespace)
|
||||
@@ -295,65 +287,18 @@ func (r *itemCollector) getResourceItems(log logrus.FieldLogger, gv schema.Group
|
||||
if selector := r.backupRequest.Spec.LabelSelector; selector != nil {
|
||||
labelSelector = metav1.FormatLabelSelector(selector)
|
||||
}
|
||||
listOptions := metav1.ListOptions{LabelSelector: labelSelector}
|
||||
|
||||
log.Info("Listing items")
|
||||
unstructuredItems := make([]unstructured.Unstructured, 0)
|
||||
|
||||
if r.pageSize > 0 {
|
||||
// If limit is positive, use a pager to split list over multiple requests
|
||||
// Use Velero's dynamic list function instead of the default
|
||||
listFunc := pager.SimplePageFunc(func(opts metav1.ListOptions) (runtime.Object, error) {
|
||||
list, err := resourceClient.List(listOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list, nil
|
||||
})
|
||||
listPager := pager.New(listFunc)
|
||||
// Use the page size defined in the server config
|
||||
// TODO allow configuration of page buffer size
|
||||
listPager.PageSize = int64(r.pageSize)
|
||||
// Add each item to temporary slice
|
||||
var items []unstructured.Unstructured
|
||||
err := listPager.EachListItem(context.Background(), listOptions, func(object runtime.Object) error {
|
||||
item, isUnstructured := object.(*unstructured.Unstructured)
|
||||
if !isUnstructured {
|
||||
// We should never hit this
|
||||
log.Error("Got type other than Unstructured from pager func")
|
||||
return nil
|
||||
}
|
||||
items = append(items, *item)
|
||||
return nil
|
||||
})
|
||||
if statusError, isStatusError := err.(*apierrors.StatusError); isStatusError && statusError.Status().Reason == metav1.StatusReasonExpired {
|
||||
log.WithError(errors.WithStack(err)).Error("Error paging item list. Falling back on unpaginated list")
|
||||
unstructuredList, err := resourceClient.List(listOptions)
|
||||
if err != nil {
|
||||
log.WithError(errors.WithStack(err)).Error("Error listing items")
|
||||
continue
|
||||
}
|
||||
items = unstructuredList.Items
|
||||
} else if err != nil {
|
||||
log.WithError(errors.WithStack(err)).Error("Error paging item list")
|
||||
continue
|
||||
}
|
||||
unstructuredItems = append(unstructuredItems, items...)
|
||||
} else {
|
||||
// If limit is not positive, do not use paging. Instead, request all items at once
|
||||
unstructuredList, err := resourceClient.List(metav1.ListOptions{LabelSelector: labelSelector})
|
||||
unstructuredItems = append(unstructuredItems, unstructuredList.Items...)
|
||||
if err != nil {
|
||||
log.WithError(errors.WithStack(err)).Error("Error listing items")
|
||||
continue
|
||||
}
|
||||
unstructuredList, err := resourceClient.List(metav1.ListOptions{LabelSelector: labelSelector})
|
||||
if err != nil {
|
||||
log.WithError(errors.WithStack(err)).Error("Error listing items")
|
||||
continue
|
||||
}
|
||||
log.Infof("Retrieved %d items", len(unstructuredList.Items))
|
||||
|
||||
log.Infof("Retrieved %d items", len(unstructuredItems))
|
||||
|
||||
// Collect items in included Namespaces
|
||||
for i := range unstructuredItems {
|
||||
item := &unstructuredItems[i]
|
||||
// collect the items
|
||||
for i := range unstructuredList.Items {
|
||||
item := &unstructuredList.Items[i]
|
||||
|
||||
if gr == kuberesource.Namespaces && !r.backupRequest.NamespaceIncludesExcludes.ShouldInclude(item.GetName()) {
|
||||
log.WithField("name", item.GetName()).Info("Skipping namespace because it's excluded")
|
||||
|
||||
@@ -17,17 +17,13 @@ limitations under the License.
|
||||
package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/logging"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -81,24 +77,7 @@ func (b *BackupBuilder) ObjectMeta(opts ...ObjectMetaOpt) *BackupBuilder {
|
||||
|
||||
// FromSchedule sets the Backup's spec and labels from the Schedule template
|
||||
func (b *BackupBuilder) FromSchedule(schedule *velerov1api.Schedule) *BackupBuilder {
|
||||
var labels = make(map[string]string)
|
||||
|
||||
// Check if there's explicit Labels defined in the Schedule object template
|
||||
// and if present then copy it to the backup object.
|
||||
if schedule.Spec.Template.Metadata.Labels != nil {
|
||||
logger := logging.DefaultLogger(logging.LogLevelFlag(logrus.InfoLevel).Parse(), logging.NewFormatFlag().Parse())
|
||||
labels = schedule.Spec.Template.Metadata.Labels
|
||||
logger.WithFields(logrus.Fields{
|
||||
"backup": fmt.Sprintf("%s/%s", b.object.GetNamespace(), b.object.GetName()),
|
||||
"labels": schedule.Spec.Template.Metadata.Labels,
|
||||
}).Info("Schedule.template.metadata.labels set - using those labels instead of schedule.labels for backup object")
|
||||
} else {
|
||||
labels = schedule.Labels
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"backup": fmt.Sprintf("%s/%s", b.object.GetNamespace(), b.object.GetName()),
|
||||
"labels": schedule.Labels,
|
||||
}).Info("No Schedule.template.metadata.labels set - using Schedule.labels for backup object")
|
||||
}
|
||||
labels := schedule.Labels
|
||||
if labels == nil {
|
||||
labels = make(map[string]string)
|
||||
}
|
||||
|
||||
@@ -60,18 +60,11 @@ func getName(image string) string {
|
||||
start = slashIndex + 1
|
||||
}
|
||||
|
||||
// If the image spec is by digest, remove the digest.
|
||||
// If it is by tag, remove the tag.
|
||||
// Otherwise (implicit :latest) leave it alone.
|
||||
// this removes the tag
|
||||
colonIndex := strings.LastIndex(image, ":")
|
||||
end := len(image)
|
||||
atIndex := strings.LastIndex(image, "@")
|
||||
if atIndex > 0 {
|
||||
end = atIndex
|
||||
} else {
|
||||
colonIndex := strings.LastIndex(image, ":")
|
||||
if colonIndex > 0 {
|
||||
end = colonIndex
|
||||
}
|
||||
if colonIndex > 0 {
|
||||
end = colonIndex
|
||||
}
|
||||
|
||||
// https://github.com/distribution/distribution/blob/main/docs/spec/api.md#overview
|
||||
|
||||
@@ -87,11 +87,6 @@ func TestGetName(t *testing.T) {
|
||||
image: "projects.registry.vmware.com/tanzu.migrator/route-2-httpproxy:myTag",
|
||||
expected: "tanzu-migrator-route-2-httpproxy",
|
||||
},
|
||||
{
|
||||
name: "pull by digest",
|
||||
image: "quay.io/vmware-tanzu/velero@sha256:a75f9e8c3ced3943515f249597be389f8233e1258d289b11184796edceaa7dab",
|
||||
expected: "vmware-tanzu-velero",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -1,77 +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 builder
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
// CustomResourceBuilder builds objects based on velero APIVersion CRDs.
|
||||
type TestCRBuilder struct {
|
||||
object *TestCR
|
||||
}
|
||||
|
||||
// ForTestCR is the constructor for a TestCRBuilder.
|
||||
func ForTestCR(crdKind, ns, name string) *TestCRBuilder {
|
||||
return &TestCRBuilder{
|
||||
object: &TestCR{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: velerov1api.SchemeGroupVersion.String(),
|
||||
Kind: crdKind,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built TestCR.
|
||||
func (b *TestCRBuilder) Result() *TestCR {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the TestCR's ObjectMeta.
|
||||
func (b *TestCRBuilder) ObjectMeta(opts ...ObjectMetaOpt) *TestCRBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
type TestCR struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
Spec TestCRSpec `json:"spec,omitempty"`
|
||||
|
||||
// +optional
|
||||
Status TestCRStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
type TestCRSpec struct {
|
||||
}
|
||||
|
||||
type TestCRStatus struct {
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright The Velero Contributors.
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kubeerrs "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
@@ -38,7 +37,6 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd/util/output"
|
||||
veleroclient "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned"
|
||||
v1 "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/collections"
|
||||
)
|
||||
|
||||
const DefaultBackupTTL time.Duration = 30 * 24 * time.Hour
|
||||
@@ -164,11 +162,6 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto
|
||||
return fmt.Errorf("A backup name is required, unless you are creating based on a schedule.")
|
||||
}
|
||||
|
||||
errs := collections.ValidateNamespaceIncludesExcludes(o.IncludeNamespaces, o.ExcludeNamespaces)
|
||||
if len(errs) > 0 {
|
||||
return kubeerrs.NewAggregate(errs)
|
||||
}
|
||||
|
||||
if o.StorageLocation != "" {
|
||||
location := &velerov1api.BackupStorageLocation{}
|
||||
if err := client.Get(context.Background(), kbclient.ObjectKey{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright The Velero Contributors.
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
def capture_backup_logs(cmd, namespace):
|
||||
if args.backup:
|
||||
log("Collecting log and information for backup: {}".format(args.backup))
|
||||
backupDescCmd = "{} --namespace={} backup describe {} --details".format(cmd, namespace, args.backup)
|
||||
capture_local(cmd=backupDescCmd, file_name="backup_describe_{}.txt".format(args.backup))
|
||||
backupLogsCmd = "{} --namespace={} backup logs {}".format(cmd, namespace, args.backup)
|
||||
capture_local(cmd=backupLogsCmd, file_name="backup_{}.log".format(args.backup))
|
||||
def capture_restore_logs(cmd, namespace):
|
||||
if args.restore:
|
||||
log("Collecting log and information for restore: {}".format(args.restore))
|
||||
restoreDescCmd = "{} --namespace={} restore describe {} --details".format(cmd, namespace, args.restore)
|
||||
capture_local(cmd=restoreDescCmd, file_name="restore_describe_{}.txt".format(args.restore))
|
||||
restoreLogsCmd = "{} --namespace={} restore logs {}".format(cmd, namespace, args.restore)
|
||||
capture_local(cmd=restoreLogsCmd, file_name="restore_{}.log".format(args.restore))
|
||||
|
||||
ns = args.namespace if args.namespace else "velero"
|
||||
output = args.output if args.output else "bundle.tar.gz"
|
||||
cmd = args.cmd if args.cmd else "velero"
|
||||
# Working dir for writing during script execution
|
||||
crshd = crashd_config(workdir="./velero-bundle")
|
||||
set_defaults(kube_config(path=args.kubeconfig, cluster_context=args.kubecontext))
|
||||
log("Collecting velero resources in namespace: {}". format(ns))
|
||||
kube_capture(what="objects", namespaces=[ns], groups=['velero.io'])
|
||||
capture_local(cmd="{} version -n {}".format(cmd, ns), file_name="version.txt")
|
||||
log("Collecting velero deployment logs in namespace: {}". format(ns))
|
||||
kube_capture(what="logs", namespaces=[ns])
|
||||
capture_backup_logs(cmd, ns)
|
||||
capture_restore_logs(cmd, ns)
|
||||
archive(output_file=output, source_paths=[crshd.workdir])
|
||||
log("Generated debug information bundle: {}".format(output))
|
||||
|
||||
|
||||
@@ -1,206 +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 debug
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/vmware-tanzu/crash-diagnostics/exec"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/client"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd"
|
||||
)
|
||||
|
||||
//go:embed cshd-scripts/velero.cshd
|
||||
var scriptBytes []byte
|
||||
|
||||
type option struct {
|
||||
// currCmd the velero command
|
||||
currCmd string
|
||||
// workdir for crashd will be $baseDir/velero-debug
|
||||
baseDir string
|
||||
// the namespace where velero server is installed
|
||||
namespace string
|
||||
// the absolute path for the log bundle to be generated
|
||||
outputPath string
|
||||
// the absolute path for the kubeconfig file that will be read by crashd for calling K8S API
|
||||
kubeconfigPath string
|
||||
// the kubecontext to be used for calling K8S API
|
||||
kubeContext string
|
||||
// optional, the name of the backup resource whose log will be packaged into the debug bundle
|
||||
backup string
|
||||
// optional, the name of the restore resource whose log will be packaged into the debug bundle
|
||||
restore string
|
||||
// optional, it controls whether to print the debug log messages when calling crashd
|
||||
verbose bool
|
||||
}
|
||||
|
||||
func (o *option) bindFlags(flags *pflag.FlagSet) {
|
||||
flags.StringVar(&o.outputPath, "output", "", "The path of the bundle tarball, by default it's ./bundle-<YYYY>-<MM>-<DD>-<HH>-<MM>-<SS>.tar.gz. Optional")
|
||||
flags.StringVar(&o.backup, "backup", "", "The name of the backup resource whose log will be collected, no backup logs will be collected if it's not set. Optional")
|
||||
flags.StringVar(&o.restore, "restore", "", "The name of the restore resource whose log will be collected, no restore logs will be collected if it's not set. Optional")
|
||||
flags.BoolVar(&o.verbose, "verbose", false, "When it's set to true the debug messages by crashd will be printed during execution. Default value is false.")
|
||||
}
|
||||
|
||||
func (o *option) asCrashdArgs() string {
|
||||
return fmt.Sprintf("output=%s,namespace=%s,basedir=%s,backup=%s,restore=%s,kubeconfig=%s,kubecontext=%s",
|
||||
o.outputPath, o.namespace, o.baseDir, o.backup, o.restore, o.kubeconfigPath, o.kubeContext)
|
||||
}
|
||||
|
||||
func (o *option) asCrashdArgMap() exec.ArgMap {
|
||||
return exec.ArgMap{
|
||||
"cmd": o.currCmd,
|
||||
"output": o.outputPath,
|
||||
"namespace": o.namespace,
|
||||
"basedir": o.baseDir,
|
||||
"backup": o.backup,
|
||||
"restore": o.restore,
|
||||
"kubeconfig": o.kubeconfigPath,
|
||||
"kubecontext": o.kubeContext,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *option) complete(f client.Factory, fs *pflag.FlagSet) error {
|
||||
if len(o.outputPath) == 0 {
|
||||
o.outputPath = fmt.Sprintf("./bundle-%s.tar.gz", time.Now().Format("2006-01-02-15-04-05"))
|
||||
}
|
||||
absOutputPath, err := filepath.Abs(o.outputPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid output path: %v", err)
|
||||
}
|
||||
o.outputPath = absOutputPath
|
||||
tmpDir, err := ioutil.TempDir("", "crashd")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.baseDir = tmpDir
|
||||
o.namespace = f.Namespace()
|
||||
kp, kc := kubeconfigAndContext(fs)
|
||||
o.currCmd, err = os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.kubeconfigPath, err = filepath.Abs(kp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid kubeconfig path: %s, %v", kp, err)
|
||||
}
|
||||
o.kubeContext = kc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *option) validate(f client.Factory) error {
|
||||
kubeClient, err := f.KubeClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l, err := kubeClient.AppsV1().Deployments(o.namespace).List(context.TODO(), metav1.ListOptions{
|
||||
LabelSelector: "component=velero",
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to check velero deployment")
|
||||
}
|
||||
if len(l.Items) == 0 {
|
||||
return fmt.Errorf("velero deployment does not exist in namespace: %s", o.namespace)
|
||||
}
|
||||
veleroClient, err := f.Client()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(o.backup) > 0 {
|
||||
if _, err := veleroClient.VeleroV1().Backups(o.namespace).Get(context.TODO(), o.backup, metav1.GetOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(o.restore) > 0 {
|
||||
if _, err := veleroClient.VeleroV1().Restores(o.namespace).Get(context.TODO(), o.restore, metav1.GetOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCommand creates a cobra command.
|
||||
func NewCommand(f client.Factory) *cobra.Command {
|
||||
o := &option{}
|
||||
c := &cobra.Command{
|
||||
Use: "debug",
|
||||
Short: "Generate debug bundle",
|
||||
Long: `Generate a tarball containing the logs of velero deployment, plugin logs, restic DaemonSet,
|
||||
specs of resources created by velero server, and optionally the logs of backup and restore.`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
flags := c.Flags()
|
||||
err := o.complete(f, flags)
|
||||
cmd.CheckError(err)
|
||||
defer func(opt *option) {
|
||||
if len(opt.baseDir) > 0 {
|
||||
if err := os.RemoveAll(opt.baseDir); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to remove temp dir: %s: %v\n", opt.baseDir, err)
|
||||
}
|
||||
}
|
||||
}(o)
|
||||
err = o.validate(f)
|
||||
cmd.CheckError(err)
|
||||
err = runCrashd(o)
|
||||
cmd.CheckError(err)
|
||||
},
|
||||
}
|
||||
o.bindFlags(c.Flags())
|
||||
return c
|
||||
}
|
||||
|
||||
func runCrashd(o *option) error {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := os.Chdir(pwd); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to go back to workdir: %v", err)
|
||||
}
|
||||
}()
|
||||
if err := os.Chdir(o.baseDir); err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.SetOutput(os.Stdout)
|
||||
if o.verbose {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
return exec.Execute("velero-debug-collector", bytes.NewReader(scriptBytes), o.asCrashdArgMap())
|
||||
}
|
||||
|
||||
func kubeconfigAndContext(fs *pflag.FlagSet) (string, string) {
|
||||
pathOpt := clientcmd.NewDefaultPathOptions()
|
||||
kubeconfig, _ := fs.GetString("kubeconfig")
|
||||
if len(kubeconfig) > 0 {
|
||||
pathOpt.LoadingRules.ExplicitPath = kubeconfig
|
||||
}
|
||||
kubecontext, _ := fs.GetString("kubecontext")
|
||||
return pathOpt.GetDefaultFilename(), kubecontext
|
||||
}
|
||||
@@ -28,8 +28,7 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/install"
|
||||
)
|
||||
|
||||
// veleroDeployment returns a Velero deployment object, selected with label and container name,
|
||||
// refer to https://github.com/vmware-tanzu/velero/issues/3961 for more information
|
||||
// veleroDeployment returns a Velero deployment object, selected using a label.
|
||||
func veleroDeployment(ctx context.Context, kubeClient kubernetes.Interface, namespace string) (*appsv1api.Deployment, error) {
|
||||
veleroLabels := labels.FormatLabels(install.Labels())
|
||||
|
||||
@@ -43,13 +42,9 @@ func veleroDeployment(ctx context.Context, kubeClient kubernetes.Interface, name
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, deploy := range deployList.Items {
|
||||
for _, container := range deploy.Spec.Template.Spec.Containers {
|
||||
if container.Name == "velero" {
|
||||
return &deploy, nil
|
||||
}
|
||||
}
|
||||
if len(deployList.Items) < 1 {
|
||||
return nil, errors.New("Velero deployment not found")
|
||||
}
|
||||
|
||||
return nil, errors.New("Velero deployment not found")
|
||||
return &deployList.Items[0], nil
|
||||
}
|
||||
|
||||
@@ -43,13 +43,12 @@ import (
|
||||
|
||||
// uninstallOptions collects all the options for uninstalling Velero from a Kubernetes cluster.
|
||||
type uninstallOptions struct {
|
||||
wait bool // deprecated
|
||||
force bool
|
||||
wait, force bool
|
||||
}
|
||||
|
||||
// BindFlags adds command line values to the options struct.
|
||||
func (o *uninstallOptions) BindFlags(flags *pflag.FlagSet) {
|
||||
flags.BoolVar(&o.wait, "wait", o.wait, "Wait for Velero uninstall to be ready. Optional. Deprecated.")
|
||||
flags.BoolVar(&o.wait, "wait", o.wait, "Wait for Velero uninstall to be ready. Optional.")
|
||||
flags.BoolVar(&o.force, "force", o.force, "Forces the Velero uninstall. Optional.")
|
||||
}
|
||||
|
||||
@@ -63,13 +62,11 @@ func NewCommand(f client.Factory) *cobra.Command {
|
||||
Long: `Uninstall Velero along with the CRDs and clusterrolebinding.
|
||||
|
||||
The '--namespace' flag can be used to specify the namespace where velero is installed (default: velero).
|
||||
Use '--wait' to wait for the Velero uninstall to be ready before proceeding.
|
||||
Use '--force' to skip the prompt confirming if you want to uninstall Velero.
|
||||
`,
|
||||
Example: ` # velero uninstall --namespace staging`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if o.wait {
|
||||
fmt.Println("Warning: the \"--wait\" option is deprecated and will be removed in a future release. The uninstall command always waits for the uninstall to complete.")
|
||||
}
|
||||
|
||||
// Confirm if not asked to force-skip confirmation
|
||||
if !o.force {
|
||||
@@ -82,7 +79,7 @@ Use '--force' to skip the prompt confirming if you want to uninstall Velero.
|
||||
|
||||
kbClient, err := f.KubebuilderClient()
|
||||
cmd.CheckError(err)
|
||||
cmd.CheckError(Run(context.Background(), kbClient, f.Namespace()))
|
||||
cmd.CheckError(Run(context.Background(), kbClient, f.Namespace(), o.wait))
|
||||
},
|
||||
}
|
||||
|
||||
@@ -91,18 +88,31 @@ Use '--force' to skip the prompt confirming if you want to uninstall Velero.
|
||||
}
|
||||
|
||||
// Run removes all components that were deployed using the Velero install command
|
||||
func Run(ctx context.Context, kbClient kbclient.Client, namespace string) error {
|
||||
// The CRDs cannot be removed until the namespace is deleted to avoid the problem in issue #3974 so if the namespace deletion fails we error out here
|
||||
if err := deleteNamespace(ctx, kbClient, namespace); err != nil {
|
||||
fmt.Printf("Errors while attempting to uninstall Velero: %q \n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
func Run(ctx context.Context, kbClient kbclient.Client, namespace string, waitToTerminate bool) error {
|
||||
var errs []error
|
||||
|
||||
// namespace
|
||||
ns := &corev1.Namespace{}
|
||||
key := kbclient.ObjectKey{Name: namespace}
|
||||
if err := kbClient.Get(ctx, key, ns); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
fmt.Printf("Velero namespace %q does not exist, skipping.\n", namespace)
|
||||
} else {
|
||||
errs = append(errs, errors.WithStack(err))
|
||||
}
|
||||
} else {
|
||||
if ns.Status.Phase == corev1.NamespaceTerminating {
|
||||
fmt.Printf("Velero namespace %q is terminating.\n", namespace)
|
||||
} else {
|
||||
if err := kbClient.Delete(ctx, ns); err != nil {
|
||||
errs = append(errs, errors.WithStack(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ClusterRoleBinding
|
||||
crb := install.ClusterRoleBinding(namespace)
|
||||
key := kbclient.ObjectKey{Name: crb.Name}
|
||||
key = kbclient.ObjectKey{Name: crb.Name}
|
||||
if err := kbClient.Get(ctx, key, crb); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
fmt.Printf("Velero ClusterRoleBinding %q does not exist, skipping.\n", crb.Name)
|
||||
@@ -149,57 +159,33 @@ func Run(ctx context.Context, kbClient kbclient.Client, namespace string) error
|
||||
}
|
||||
}
|
||||
|
||||
if waitToTerminate && len(ns.Name) != 0 {
|
||||
fmt.Println("Waiting for Velero uninstall to complete. You may safely press ctrl-c to stop waiting - uninstall will continue in the background.")
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
checkFunc := func() {
|
||||
err := kbClient.Get(ctx, key, ns)
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
fmt.Print("\n")
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
errs = append(errs, errors.WithStack(err))
|
||||
}
|
||||
fmt.Print(".")
|
||||
}
|
||||
|
||||
wait.Until(checkFunc, 5*time.Millisecond, ctx.Done())
|
||||
}
|
||||
|
||||
if kubeerrs.NewAggregate(errs) != nil {
|
||||
fmt.Printf("Errors while attempting to uninstall Velero: %q \n", kubeerrs.NewAggregate(errs))
|
||||
fmt.Printf("Errors while attempting to uninstall Velero: %q", kubeerrs.NewAggregate(errs))
|
||||
return kubeerrs.NewAggregate(errs)
|
||||
}
|
||||
|
||||
fmt.Println("Velero uninstalled ⛵")
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteNamespace(ctx context.Context, kbClient kbclient.Client, namespace string) error {
|
||||
ns := &corev1.Namespace{}
|
||||
key := kbclient.ObjectKey{Name: namespace}
|
||||
if err := kbClient.Get(ctx, key, ns); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
fmt.Printf("Velero namespace %q does not exist, skipping.\n", namespace)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if err := kbClient.Delete(ctx, ns); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
fmt.Printf("Velero namespace %q does not exist, skipping.\n", namespace)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Waiting for velero namespace %q to be deleted\n", namespace)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
var err error
|
||||
checkFunc := func() {
|
||||
if err = kbClient.Get(ctx, key, ns); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
fmt.Print("\n")
|
||||
err = nil
|
||||
}
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
fmt.Print(".")
|
||||
}
|
||||
|
||||
// Must wait until the namespace is deleted to avoid the issue https://github.com/vmware-tanzu/velero/issues/3974
|
||||
wait.Until(checkFunc, 5*time.Millisecond, ctx.Done())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Velero namespace %q deleted\n", namespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -90,9 +90,8 @@ const (
|
||||
defaultResourceTerminatingTimeout = 10 * time.Minute
|
||||
|
||||
// server's client default qps and burst
|
||||
defaultClientQPS float32 = 20.0
|
||||
defaultClientBurst int = 30
|
||||
defaultClientPageSize int = 500
|
||||
defaultClientQPS float32 = 20.0
|
||||
defaultClientBurst int = 30
|
||||
|
||||
defaultProfilerAddress = "localhost:6060"
|
||||
|
||||
@@ -116,7 +115,6 @@ type serverConfig struct {
|
||||
disabledControllers []string
|
||||
clientQPS float32
|
||||
clientBurst int
|
||||
clientPageSize int
|
||||
profilerAddress string
|
||||
formatFlag *logging.FormatFlag
|
||||
defaultResticMaintenanceFrequency time.Duration
|
||||
@@ -144,7 +142,6 @@ func NewCommand(f client.Factory) *cobra.Command {
|
||||
restoreResourcePriorities: defaultRestorePriorities,
|
||||
clientQPS: defaultClientQPS,
|
||||
clientBurst: defaultClientBurst,
|
||||
clientPageSize: defaultClientPageSize,
|
||||
profilerAddress: defaultProfilerAddress,
|
||||
resourceTerminatingTimeout: defaultResourceTerminatingTimeout,
|
||||
formatFlag: logging.NewFormatFlag(),
|
||||
@@ -208,7 +205,6 @@ func NewCommand(f client.Factory) *cobra.Command {
|
||||
command.Flags().Var(&volumeSnapshotLocations, "default-volume-snapshot-locations", "List of unique volume providers and default volume snapshot location (provider1:location-01,provider2:location-02,...)")
|
||||
command.Flags().Float32Var(&config.clientQPS, "client-qps", config.clientQPS, "Maximum number of requests per second by the server to the Kubernetes API once the burst limit has been reached.")
|
||||
command.Flags().IntVar(&config.clientBurst, "client-burst", config.clientBurst, "Maximum number of requests by the server to the Kubernetes API in a short period of time.")
|
||||
command.Flags().IntVar(&config.clientPageSize, "client-page-size", config.clientPageSize, "Page size of requests by the server to the Kubernetes API when listing objects during a backup. Set to 0 to disable paging.")
|
||||
command.Flags().StringVar(&config.profilerAddress, "profiler-address", config.profilerAddress, "The address to expose the pprof profiler.")
|
||||
command.Flags().DurationVar(&config.resourceTerminatingTimeout, "terminating-resource-timeout", config.resourceTerminatingTimeout, "How long to wait on persistent volumes and namespaces to terminate during a restore before timing out.")
|
||||
command.Flags().DurationVar(&config.defaultBackupTTL, "default-backup-ttl", config.defaultBackupTTL, "How long to wait by default before backups can be garbage collected.")
|
||||
@@ -253,10 +249,6 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s
|
||||
}
|
||||
f.SetClientBurst(config.clientBurst)
|
||||
|
||||
if config.clientPageSize < 0 {
|
||||
return nil, errors.New("client-page-size must not be negative")
|
||||
}
|
||||
|
||||
kubeClient, err := f.KubeClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -303,8 +295,7 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s
|
||||
corev1api.AddToScheme(scheme)
|
||||
|
||||
mgr, err := ctrl.NewManager(clientConfig, ctrl.Options{
|
||||
Scheme: scheme,
|
||||
Namespace: f.Namespace(),
|
||||
Scheme: scheme,
|
||||
})
|
||||
if err != nil {
|
||||
cancelFunc()
|
||||
@@ -619,7 +610,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
||||
s.resticManager,
|
||||
s.config.podVolumeOperationTimeout,
|
||||
s.config.defaultVolumesToRestic,
|
||||
s.config.clientPageSize,
|
||||
)
|
||||
cmd.CheckError(err)
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -42,12 +41,7 @@ import (
|
||||
var ErrNotFound = errors.New("file not found")
|
||||
|
||||
func Stream(ctx context.Context, kbClient kbclient.Client, namespace, name string, kind velerov1api.DownloadTargetKind, w io.Writer, timeout time.Duration, insecureSkipTLSVerify bool, caCertFile string) error {
|
||||
uuid, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
reqName := fmt.Sprintf("%s-%s", name, uuid.String())
|
||||
reqName := fmt.Sprintf("%s-%s", name, time.Now().Format("20060102150405"))
|
||||
created := builder.ForDownloadRequest(namespace, reqName).Target(kind, name).Result()
|
||||
|
||||
if err := kbClient.Create(context.Background(), created, &kbclient.CreateOptions{}); err != nil {
|
||||
|
||||
@@ -25,8 +25,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/klog"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd/cli/debug"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/client"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd/cli/backup"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd/cli/backuplocation"
|
||||
@@ -118,7 +116,6 @@ operations can also be performed as 'velero backup get' and 'velero schedule cre
|
||||
bug.NewCommand(),
|
||||
backuplocation.NewCommand(f),
|
||||
snapshotlocation.NewCommand(f),
|
||||
debug.NewCommand(f),
|
||||
)
|
||||
|
||||
// init and add the klog flags
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 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.
|
||||
@@ -424,7 +424,7 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg
|
||||
}
|
||||
|
||||
// validate the included/excluded namespaces
|
||||
for _, err := range collections.ValidateNamespaceIncludesExcludes(request.Spec.IncludedNamespaces, request.Spec.ExcludedNamespaces) {
|
||||
for _, err := range collections.ValidateIncludesExcludes(request.Spec.IncludedNamespaces, request.Spec.ExcludedNamespaces) {
|
||||
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid included/excluded namespace lists: %v", err))
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ import (
|
||||
persistencemocks "github.com/vmware-tanzu/velero/pkg/persistence/mocks"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt"
|
||||
pluginmocks "github.com/vmware-tanzu/velero/pkg/plugin/mocks"
|
||||
backupitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/logging"
|
||||
@@ -58,7 +58,7 @@ type fakeBackupper struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (b *fakeBackupper) Backup(logger logrus.FieldLogger, backup *pkgbackup.Request, backupFile io.Writer, actions []backupitemactionv2.BackupItemAction, volumeSnapshotterGetter pkgbackup.VolumeSnapshotterGetter) error {
|
||||
func (b *fakeBackupper) Backup(logger logrus.FieldLogger, backup *pkgbackup.Request, backupFile io.Writer, actions []velero.BackupItemAction, volumeSnapshotterGetter pkgbackup.VolumeSnapshotterGetter) error {
|
||||
args := b.Called(logger, backup, backupFile, actions, volumeSnapshotterGetter)
|
||||
return args.Error(0)
|
||||
}
|
||||
@@ -825,7 +825,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
|
||||
pluginManager.On("GetBackupItemActions").Return(nil, nil)
|
||||
pluginManager.On("CleanupClients").Return(nil)
|
||||
backupper.On("Backup", mock.Anything, mock.Anything, mock.Anything, []backupitemactionv2.BackupItemAction(nil), pluginManager).Return(nil)
|
||||
backupper.On("Backup", mock.Anything, mock.Anything, mock.Anything, []velero.BackupItemAction(nil), pluginManager).Return(nil)
|
||||
backupStore.On("BackupExists", test.backupLocation.Spec.StorageType.ObjectStorage.Bucket, test.backup.Name).Return(test.backupExists, test.existenceCheckError)
|
||||
|
||||
// Ensure we have a CompletionTimestamp when uploading and that the backup name matches the backup in the object store.
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned"
|
||||
snapshotter "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1beta1"
|
||||
snapshotv1beta1listers "github.com/kubernetes-csi/external-snapshotter/client/v4/listers/volumesnapshot/v1beta1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -39,6 +40,7 @@ import (
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
pkgbackup "github.com/vmware-tanzu/velero/pkg/backup"
|
||||
"github.com/vmware-tanzu/velero/pkg/discovery"
|
||||
"github.com/vmware-tanzu/velero/pkg/features"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1"
|
||||
velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1"
|
||||
@@ -46,7 +48,7 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/metrics"
|
||||
"github.com/vmware-tanzu/velero/pkg/persistence"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt"
|
||||
volumesnapshotter "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
@@ -304,11 +306,11 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR
|
||||
if len(actions) > 0 {
|
||||
// Download the tarball
|
||||
backupFile, err := downloadToTempFile(backup.Name, backupStore, log)
|
||||
defer closeAndRemoveFile(backupFile, c.logger)
|
||||
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("Unable to download tarball for backup %s, skipping associated DeleteItemAction plugins", backup.Name)
|
||||
} else {
|
||||
defer closeAndRemoveFile(backupFile, c.logger)
|
||||
ctx := &delete.Context{
|
||||
Backup: backup,
|
||||
BackupReader: backupFile,
|
||||
@@ -333,7 +335,7 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR
|
||||
if snapshots, err := backupStore.GetBackupVolumeSnapshots(backup.Name); err != nil {
|
||||
errs = append(errs, errors.Wrap(err, "error getting backup's volume snapshots").Error())
|
||||
} else {
|
||||
volumeSnapshotters := make(map[string]volumesnapshotter.VolumeSnapshotter)
|
||||
volumeSnapshotters := make(map[string]velero.VolumeSnapshotter)
|
||||
|
||||
for _, snapshot := range snapshots {
|
||||
log.WithField("providerSnapshotID", snapshot.Status.ProviderSnapshotID).Info("Removing snapshot associated with backup")
|
||||
@@ -368,6 +370,22 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR
|
||||
}
|
||||
}
|
||||
|
||||
if features.IsEnabled(velerov1api.CSIFeatureFlag) {
|
||||
log.Info("Removing CSI volumesnapshots")
|
||||
if csiErrs := deleteCSIVolumeSnapshots(backup.Name, c.csiSnapshotLister, c.csiSnapshotClient.SnapshotV1beta1(), log); len(csiErrs) > 0 {
|
||||
for _, err := range csiErrs {
|
||||
errs = append(errs, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Removing CSI volumesnapshotcontents")
|
||||
if csiErrs := deleteCSIVolumeSnapshotContents(backup.Name, c.csiSnapshotContentLister, c.csiSnapshotClient.SnapshotV1beta1(), log); len(csiErrs) > 0 {
|
||||
for _, err := range csiErrs {
|
||||
errs = append(errs, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Removing restores")
|
||||
if restores, err := c.restoreLister.Restores(backup.Namespace).List(labels.Everything()); err != nil {
|
||||
log.WithError(errors.WithStack(err)).Error("Error listing restore API objects")
|
||||
@@ -433,7 +451,7 @@ func volumeSnapshotterForSnapshotLocation(
|
||||
namespace, snapshotLocationName string,
|
||||
snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister,
|
||||
pluginManager clientmgmt.Manager,
|
||||
) (volumesnapshotter.VolumeSnapshotter, error) {
|
||||
) (velero.VolumeSnapshotter, error) {
|
||||
snapshotLocation, err := snapshotLocationLister.VolumeSnapshotLocations(namespace).Get(snapshotLocationName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error getting volume snapshot location %s", snapshotLocationName)
|
||||
@@ -496,6 +514,82 @@ func (c *backupDeletionController) deleteResticSnapshots(backup *velerov1api.Bac
|
||||
return errs
|
||||
}
|
||||
|
||||
func setVolumeSnapshotContentDeletionPolicy(vscName string, csiClient snapshotter.SnapshotV1beta1Interface, log *logrus.Entry) error {
|
||||
log.Infof("Setting DeletionPolicy of CSI volumesnapshotcontent %s to Delete", vscName)
|
||||
pb := []byte(`{"spec":{"deletionPolicy":"Delete"}}`)
|
||||
_, err := csiClient.VolumeSnapshotContents().Patch(context.TODO(), vscName, types.MergePatchType, pb, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteCSIVolumeSnapshots(backupName string, csiSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister,
|
||||
csiClient snapshotter.SnapshotV1beta1Interface, log *logrus.Entry) []error {
|
||||
errs := []error{}
|
||||
|
||||
selector := label.NewSelectorForBackup(backupName)
|
||||
csiVolSnaps, err := csiSnapshotLister.List(selector)
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
log.Infof("Deleting %d CSI volumesnapshots", len(csiVolSnaps))
|
||||
for _, csiVS := range csiVolSnaps {
|
||||
log.Infof("Deleting CSI volumesnapshot %s/%s", csiVS.Namespace, csiVS.Name)
|
||||
if csiVS.Status != nil && csiVS.Status.BoundVolumeSnapshotContentName != nil {
|
||||
// we patch the DeletionPolicy of the volumesnapshotcontent to set it to Delete.
|
||||
// This ensures that the volume snapshot in the storage provider is also deleted.
|
||||
err := setVolumeSnapshotContentDeletionPolicy(*csiVS.Status.BoundVolumeSnapshotContentName, csiClient, log)
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
log.Errorf("Skipping deletion of volumesnapshot %s/%s", csiVS.Namespace, csiVS.Name)
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
err := csiClient.VolumeSnapshots(csiVS.Namespace).Delete(context.TODO(), csiVS.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func deleteCSIVolumeSnapshotContents(backupName string, csiVSCLister snapshotv1beta1listers.VolumeSnapshotContentLister,
|
||||
csiClient snapshotter.SnapshotV1beta1Interface, log *logrus.Entry) []error {
|
||||
errs := []error{}
|
||||
selector := label.NewSelectorForBackup(backupName)
|
||||
csiVolSnapConts, err := csiVSCLister.List(selector)
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
// It is possible that by the time deleteCSIVolumeSnapshotContents is called after deleteCSIVolumeSnapshots
|
||||
// that deletion of VSCs hasn't been completed, by the snapshot-controller (one of the CSI components).
|
||||
// For that reason the csiVSCLister returned VSCs that are yet to be deleted. To handle this scenario,
|
||||
// we swallow `IsNotFound` errors from the setVolumeSnapshotContentDeletionPolicy function and the
|
||||
// csiClient.VolumeSnapshotContents().Delete(...)
|
||||
log.Infof("Deleting %d CSI volumesnapshotcontents", len(csiVolSnapConts))
|
||||
for _, snapCont := range csiVolSnapConts {
|
||||
err := setVolumeSnapshotContentDeletionPolicy(snapCont.Name, csiClient, log)
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
log.Errorf("Failed to set DeletionPolicy on volumesnapshotcontent %s. Skipping deletion", snapCont.Name)
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
if apierrors.IsNotFound(err) {
|
||||
log.Infof("volumesnapshotcontent %s not found", snapCont.Name)
|
||||
continue
|
||||
}
|
||||
log.Infof("Deleting volumesnapshotcontent %s", snapCont.Name)
|
||||
err = csiClient.VolumeSnapshotContents().Delete(context.TODO(), snapCont.Name, metav1.DeleteOptions{})
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
const deleteBackupRequestMaxAge = 24 * time.Hour
|
||||
|
||||
func (c *backupDeletionController) deleteExpiredRequests() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 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.
|
||||
@@ -24,6 +24,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
snapshotv1beta1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1"
|
||||
snapshotFake "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/fake"
|
||||
snapshotv1beta1informers "github.com/kubernetes-csi/external-snapshotter/client/v4/informers/externalversions"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -45,7 +48,7 @@ import (
|
||||
persistencemocks "github.com/vmware-tanzu/velero/pkg/persistence/mocks"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt"
|
||||
pluginmocks "github.com/vmware-tanzu/velero/pkg/plugin/mocks"
|
||||
deleteitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/deleteitemaction/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
"github.com/vmware-tanzu/velero/pkg/volume"
|
||||
@@ -802,7 +805,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
||||
|
||||
pluginManager := &pluginmocks.Manager{}
|
||||
pluginManager.On("GetVolumeSnapshotter", "provider-1").Return(td.volumeSnapshotter, nil)
|
||||
pluginManager.On("GetDeleteItemActions").Return([]deleteitemactionv2.DeleteItemAction{}, nil)
|
||||
pluginManager.On("GetDeleteItemActions").Return([]velero.DeleteItemAction{}, nil)
|
||||
pluginManager.On("CleanupClients")
|
||||
td.controller.newPluginManager = func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }
|
||||
|
||||
@@ -932,7 +935,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
||||
|
||||
pluginManager := &pluginmocks.Manager{}
|
||||
pluginManager.On("GetVolumeSnapshotter", "provider-1").Return(td.volumeSnapshotter, nil)
|
||||
pluginManager.On("GetDeleteItemActions").Return([]deleteitemactionv2.DeleteItemAction{new(mocks.DeleteItemAction)}, nil)
|
||||
pluginManager.On("GetDeleteItemActions").Return([]velero.DeleteItemAction{new(mocks.DeleteItemAction)}, nil)
|
||||
pluginManager.On("CleanupClients")
|
||||
td.controller.newPluginManager = func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }
|
||||
|
||||
@@ -1151,3 +1154,342 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetVolumeSnapshotContentDeletionPolicy(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
inputVSCName string
|
||||
objs []runtime.Object
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "should update DeletionPolicy of a VSC from retain to delete",
|
||||
inputVSCName: "retainVSC",
|
||||
objs: []runtime.Object{
|
||||
&snapshotv1beta1api.VolumeSnapshotContent{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "retainVSC",
|
||||
},
|
||||
Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{
|
||||
DeletionPolicy: snapshotv1beta1api.VolumeSnapshotContentRetain,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "should be a no-op updating if DeletionPolicy of a VSC is already Delete",
|
||||
inputVSCName: "deleteVSC",
|
||||
objs: []runtime.Object{
|
||||
&snapshotv1beta1api.VolumeSnapshotContent{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "deleteVSC",
|
||||
},
|
||||
Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{
|
||||
DeletionPolicy: snapshotv1beta1api.VolumeSnapshotContentDelete,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "should update DeletionPolicy of a VSC with no DeletionPolicy",
|
||||
inputVSCName: "nothingVSC",
|
||||
objs: []runtime.Object{
|
||||
&snapshotv1beta1api.VolumeSnapshotContent{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "nothingVSC",
|
||||
},
|
||||
Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{},
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "should return not found error if supplied VSC does not exist",
|
||||
inputVSCName: "does-not-exist",
|
||||
objs: []runtime.Object{},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
log := velerotest.NewLogger().WithFields(
|
||||
logrus.Fields{
|
||||
"unit-test": "unit-test",
|
||||
},
|
||||
)
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
fakeClient := snapshotFake.NewSimpleClientset(tc.objs...)
|
||||
err := setVolumeSnapshotContentDeletionPolicy(tc.inputVSCName, fakeClient.SnapshotV1beta1(), log)
|
||||
if tc.expectError {
|
||||
assert.NotNil(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
actual, err := fakeClient.SnapshotV1beta1().VolumeSnapshotContents().Get(context.TODO(), tc.inputVSCName, metav1.GetOptions{})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, snapshotv1beta1api.VolumeSnapshotContentDelete, actual.Spec.DeletionPolicy)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteCSIVolumeSnapshots(t *testing.T) {
|
||||
//Backup1
|
||||
ns1VS1VSCName := "ns1vs1vsc"
|
||||
ns1VS1VSC := snapshotv1beta1api.VolumeSnapshotContent{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ns1VS1VSCName,
|
||||
},
|
||||
Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{
|
||||
DeletionPolicy: snapshotv1beta1api.VolumeSnapshotContentRetain,
|
||||
},
|
||||
}
|
||||
ns1VS1 := snapshotv1beta1api.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vs1",
|
||||
Namespace: "ns1",
|
||||
Labels: map[string]string{
|
||||
velerov1api.BackupNameLabel: "backup1",
|
||||
},
|
||||
},
|
||||
Status: &snapshotv1beta1api.VolumeSnapshotStatus{
|
||||
BoundVolumeSnapshotContentName: &ns1VS1VSCName,
|
||||
},
|
||||
}
|
||||
|
||||
ns1VS2VSCName := "ns1vs2vsc"
|
||||
ns1VS2VSC := snapshotv1beta1api.VolumeSnapshotContent{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ns1VS2VSCName,
|
||||
},
|
||||
Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{
|
||||
DeletionPolicy: snapshotv1beta1api.VolumeSnapshotContentRetain,
|
||||
},
|
||||
}
|
||||
ns1VS2 := snapshotv1beta1api.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vs2",
|
||||
Namespace: "ns1",
|
||||
Labels: map[string]string{
|
||||
velerov1api.BackupNameLabel: "backup1",
|
||||
},
|
||||
},
|
||||
Status: &snapshotv1beta1api.VolumeSnapshotStatus{
|
||||
BoundVolumeSnapshotContentName: &ns1VS2VSCName,
|
||||
},
|
||||
}
|
||||
|
||||
ns2VS1VSCName := "ns2vs1vsc"
|
||||
ns2VS1VSC := snapshotv1beta1api.VolumeSnapshotContent{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ns2VS1VSCName,
|
||||
},
|
||||
Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{
|
||||
DeletionPolicy: snapshotv1beta1api.VolumeSnapshotContentRetain,
|
||||
},
|
||||
}
|
||||
ns2VS1 := snapshotv1beta1api.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vs1",
|
||||
Namespace: "ns2",
|
||||
Labels: map[string]string{
|
||||
velerov1api.BackupNameLabel: "backup1",
|
||||
},
|
||||
},
|
||||
Status: &snapshotv1beta1api.VolumeSnapshotStatus{
|
||||
BoundVolumeSnapshotContentName: &ns2VS1VSCName,
|
||||
},
|
||||
}
|
||||
|
||||
ns2VS2VSCName := "ns2vs2vsc"
|
||||
ns2VS2VSC := snapshotv1beta1api.VolumeSnapshotContent{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ns2VS2VSCName,
|
||||
},
|
||||
Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{
|
||||
DeletionPolicy: snapshotv1beta1api.VolumeSnapshotContentRetain,
|
||||
},
|
||||
}
|
||||
ns2VS2 := snapshotv1beta1api.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vs2",
|
||||
Namespace: "ns2",
|
||||
Labels: map[string]string{
|
||||
velerov1api.BackupNameLabel: "backup1",
|
||||
},
|
||||
},
|
||||
Status: &snapshotv1beta1api.VolumeSnapshotStatus{
|
||||
BoundVolumeSnapshotContentName: &ns2VS2VSCName,
|
||||
},
|
||||
}
|
||||
|
||||
// Backup2
|
||||
ns1NilStatusVS := snapshotv1beta1api.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ns1NilStatusVS",
|
||||
Namespace: "ns2",
|
||||
Labels: map[string]string{
|
||||
velerov1api.BackupNameLabel: "backup2",
|
||||
},
|
||||
},
|
||||
Status: nil,
|
||||
}
|
||||
|
||||
// Backup3
|
||||
ns1NilBoundVSCVS := snapshotv1beta1api.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ns1NilBoundVSCVS",
|
||||
Namespace: "ns2",
|
||||
Labels: map[string]string{
|
||||
velerov1api.BackupNameLabel: "backup3",
|
||||
},
|
||||
},
|
||||
Status: &snapshotv1beta1api.VolumeSnapshotStatus{
|
||||
BoundVolumeSnapshotContentName: nil,
|
||||
},
|
||||
}
|
||||
|
||||
// Backup4
|
||||
notFound := "not-found"
|
||||
ns1NonExistentVSCVS := snapshotv1beta1api.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ns1NonExistentVSCVS",
|
||||
Namespace: "ns2",
|
||||
Labels: map[string]string{
|
||||
velerov1api.BackupNameLabel: "backup3",
|
||||
},
|
||||
},
|
||||
Status: &snapshotv1beta1api.VolumeSnapshotStatus{
|
||||
BoundVolumeSnapshotContentName: ¬Found,
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
backupName string
|
||||
objs []runtime.Object
|
||||
}{
|
||||
{
|
||||
name: "should delete volumesnapshots bound to existing volumesnapshotcontent",
|
||||
backupName: "backup1",
|
||||
objs: []runtime.Object{&ns1VS1VSC, &ns1VS1, &ns1VS2VSC, &ns1VS2, &ns2VS1VSC, &ns2VS1, &ns2VS2VSC, &ns2VS2},
|
||||
},
|
||||
{
|
||||
name: "should delete volumesnapshots with nil status",
|
||||
backupName: "backup2",
|
||||
objs: []runtime.Object{&ns1NilStatusVS},
|
||||
},
|
||||
{
|
||||
name: "should delete volumesnapshots with nil BoundVolumeSnapshotContentName",
|
||||
backupName: "backup3",
|
||||
objs: []runtime.Object{&ns1NilBoundVSCVS},
|
||||
},
|
||||
{
|
||||
name: "should delete volumesnapshots bound to non-existent volumesnapshotcontents",
|
||||
backupName: "backup4",
|
||||
objs: []runtime.Object{&ns1NonExistentVSCVS},
|
||||
},
|
||||
{
|
||||
name: "should be a no-op when there are no volumesnapshots to delete",
|
||||
backupName: "backup-no-vs",
|
||||
objs: []runtime.Object{},
|
||||
},
|
||||
}
|
||||
|
||||
log := velerotest.NewLogger().WithFields(
|
||||
logrus.Fields{
|
||||
"unit-test": "unit-test",
|
||||
},
|
||||
)
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
fakeClient := snapshotFake.NewSimpleClientset(tc.objs...)
|
||||
fakeSharedInformer := snapshotv1beta1informers.NewSharedInformerFactoryWithOptions(fakeClient, 0)
|
||||
for _, o := range tc.objs {
|
||||
fakeSharedInformer.Snapshot().V1beta1().VolumeSnapshots().Informer().GetStore().Add(o)
|
||||
}
|
||||
errs := deleteCSIVolumeSnapshots(tc.backupName, fakeSharedInformer.Snapshot().V1beta1().VolumeSnapshots().Lister(), fakeClient.SnapshotV1beta1(), log)
|
||||
assert.Empty(t, errs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteCSIVolumeSnapshotContents(t *testing.T) {
|
||||
retainVSC := snapshotv1beta1api.VolumeSnapshotContent{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "retainVSC",
|
||||
Labels: map[string]string{
|
||||
velerov1api.BackupNameLabel: "backup1",
|
||||
},
|
||||
},
|
||||
Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{
|
||||
DeletionPolicy: snapshotv1beta1api.VolumeSnapshotContentRetain,
|
||||
},
|
||||
}
|
||||
deleteVSC := snapshotv1beta1api.VolumeSnapshotContent{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "deleteVSC",
|
||||
Labels: map[string]string{
|
||||
velerov1api.BackupNameLabel: "backup2",
|
||||
},
|
||||
},
|
||||
Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{
|
||||
DeletionPolicy: snapshotv1beta1api.VolumeSnapshotContentDelete,
|
||||
},
|
||||
}
|
||||
|
||||
nothingVSC := snapshotv1beta1api.VolumeSnapshotContent{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "nothingVSC",
|
||||
Labels: map[string]string{
|
||||
velerov1api.BackupNameLabel: "backup3",
|
||||
},
|
||||
},
|
||||
Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
backupName string
|
||||
objs []runtime.Object
|
||||
}{
|
||||
{
|
||||
name: "should delete volumesnapshotcontent with DeletionPolicy Retain",
|
||||
backupName: "backup1",
|
||||
objs: []runtime.Object{&retainVSC},
|
||||
},
|
||||
{
|
||||
name: "should delete volumesnapshotcontent with DeletionPolicy Delete",
|
||||
backupName: "backup3",
|
||||
objs: []runtime.Object{&deleteVSC},
|
||||
},
|
||||
{
|
||||
name: "should delete volumesnapshotcontent with No DeletionPolicy",
|
||||
backupName: "backup3",
|
||||
objs: []runtime.Object{¬hingVSC},
|
||||
},
|
||||
{
|
||||
name: "should return no error when backup has no volumesnapshotconents",
|
||||
backupName: "backup-with-no-vsc",
|
||||
objs: []runtime.Object{},
|
||||
},
|
||||
}
|
||||
log := velerotest.NewLogger().WithFields(
|
||||
logrus.Fields{
|
||||
"unit-test": "unit-test",
|
||||
},
|
||||
)
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
fakeClient := snapshotFake.NewSimpleClientset(tc.objs...)
|
||||
fakeSharedInformer := snapshotv1beta1informers.NewSharedInformerFactoryWithOptions(fakeClient, 0)
|
||||
for _, o := range tc.objs {
|
||||
fakeSharedInformer.Snapshot().V1beta1().VolumeSnapshotContents().Informer().GetStore().Add(o)
|
||||
}
|
||||
|
||||
errs := deleteCSIVolumeSnapshotContents(tc.backupName, fakeSharedInformer.Snapshot().V1beta1().VolumeSnapshotContents().Lister(), fakeClient.SnapshotV1beta1(), log)
|
||||
assert.Empty(t, errs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ func (c *backupSyncController) run() {
|
||||
c.deleteOrphanedBackups(location.Name, backupStoreBackups, log)
|
||||
|
||||
// update the location's last-synced time field
|
||||
statusPatch := client.MergeFrom(location.DeepCopy())
|
||||
statusPatch := client.MergeFrom(location.DeepCopyObject())
|
||||
location.Status.LastSyncedTime = &metav1.Time{Time: time.Now().UTC()}
|
||||
if err := c.kbClient.Status().Patch(context.Background(), &location, statusPatch); err != nil {
|
||||
log.WithError(errors.WithStack(err)).Error("Error patching backup location's last-synced time")
|
||||
|
||||
@@ -553,9 +553,6 @@ func downloadToTempFile(backupName string, backupStore persistence.BackupStore,
|
||||
|
||||
n, err := io.Copy(file, readCloser)
|
||||
if err != nil {
|
||||
//Temporary file has been created if we go here. And some problems occurs such as network interruption and
|
||||
//so on. So we close and remove temporary file first to prevent residual file.
|
||||
closeAndRemoveFile(file, logger)
|
||||
return nil, errors.Wrap(err, "error copying Backup to temp file")
|
||||
}
|
||||
|
||||
@@ -567,7 +564,6 @@ func downloadToTempFile(backupName string, backupStore persistence.BackupStore,
|
||||
}).Debug("Copied Backup to file")
|
||||
|
||||
if _, err := file.Seek(0, 0); err != nil {
|
||||
closeAndRemoveFile(file, logger)
|
||||
return nil, errors.Wrap(err, "error resetting Backup file offset")
|
||||
}
|
||||
|
||||
|
||||
@@ -276,11 +276,11 @@ func (c *scheduleController) submitBackupIfDue(item *api.Schedule, cronSchedule
|
||||
}
|
||||
|
||||
func getNextRunTime(schedule *api.Schedule, cronSchedule cron.Schedule, asOf time.Time) (bool, time.Time) {
|
||||
// get the latest run time (if the schedule hasn't run yet, this will be the zero value which will trigger
|
||||
// an immediate backup)
|
||||
var lastBackupTime time.Time
|
||||
if schedule.Status.LastBackup != nil {
|
||||
lastBackupTime = schedule.Status.LastBackup.Time
|
||||
} else {
|
||||
lastBackupTime = schedule.CreationTimestamp.Time
|
||||
}
|
||||
|
||||
nextRunTime := cronSchedule.Next(lastBackupTime)
|
||||
|
||||
@@ -274,7 +274,7 @@ func TestGetNextRunTime(t *testing.T) {
|
||||
{
|
||||
name: "first run",
|
||||
schedule: defaultSchedule(),
|
||||
expectedDue: false,
|
||||
expectedDue: true,
|
||||
expectedNextRunTimeOffset: "5m",
|
||||
},
|
||||
{
|
||||
@@ -319,9 +319,6 @@ func TestGetNextRunTime(t *testing.T) {
|
||||
require.NoError(t, err, "unable to parse test.lastRanOffset: %v", err)
|
||||
|
||||
test.schedule.Status.LastBackup = &metav1.Time{Time: testClock.Now().Add(-offsetDuration)}
|
||||
test.schedule.CreationTimestamp = *test.schedule.Status.LastBackup
|
||||
} else {
|
||||
test.schedule.CreationTimestamp = metav1.Time{Time: testClock.Now()}
|
||||
}
|
||||
|
||||
nextRunTimeOffset, err := time.ParseDuration(test.expectedNextRunTimeOffset)
|
||||
@@ -329,11 +326,11 @@ func TestGetNextRunTime(t *testing.T) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// calculate expected next run time (if the schedule hasn't run yet, this
|
||||
// will be the zero value which will trigger an immediate backup)
|
||||
var baseTime time.Time
|
||||
if test.lastRanOffset != "" {
|
||||
baseTime = test.schedule.Status.LastBackup.Time
|
||||
} else {
|
||||
baseTime = test.schedule.CreationTimestamp.Time
|
||||
}
|
||||
expectedNextRunTime := baseTime.Add(nextRunTimeOffset)
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@ var kindToResource = map[string]string{
|
||||
"Deployment": "deployments",
|
||||
"DaemonSet": "daemonsets",
|
||||
"Secret": "secrets",
|
||||
"ConfigMap": "configmaps",
|
||||
"BackupStorageLocation": "backupstoragelocations",
|
||||
"VolumeSnapshotLocation": "volumesnapshotlocations",
|
||||
}
|
||||
|
||||
@@ -33,7 +33,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/generated/clientset/versioned/scheme"
|
||||
objectstorev2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/objectstore/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/volume"
|
||||
)
|
||||
|
||||
@@ -80,16 +80,16 @@ type BackupStore interface {
|
||||
const DownloadURLTTL = 10 * time.Minute
|
||||
|
||||
type objectBackupStore struct {
|
||||
objectStore objectstorev2.ObjectStore
|
||||
objectStore velero.ObjectStore
|
||||
bucket string
|
||||
layout *ObjectStoreLayout
|
||||
logger logrus.FieldLogger
|
||||
}
|
||||
|
||||
// ObjectStoreGetter is a type that can get a objectstorev2.ObjectStore
|
||||
// ObjectStoreGetter is a type that can get a velero.ObjectStore
|
||||
// from a provider name.
|
||||
type ObjectStoreGetter interface {
|
||||
GetObjectStore(provider string) (objectstorev2.ObjectStore, error)
|
||||
GetObjectStore(provider string) (velero.ObjectStore, error)
|
||||
}
|
||||
|
||||
// ObjectBackupStoreGetter is a type that can get a velero.BackupStore for a
|
||||
@@ -326,7 +326,7 @@ func (s *objectBackupStore) GetBackupVolumeSnapshots(name string) ([]*volume.Sna
|
||||
|
||||
// tryGet returns the object with the given key if it exists, nil if it does not exist,
|
||||
// or an error if it was unable to check existence or get the object.
|
||||
func tryGet(objectStore objectstorev2.ObjectStore, bucket, key string) (io.ReadCloser, error) {
|
||||
func tryGet(objectStore velero.ObjectStore, bucket, key string) (io.ReadCloser, error) {
|
||||
exists, err := objectStore.ObjectExists(bucket, key)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
@@ -494,7 +494,7 @@ func seekToBeginning(r io.Reader) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func seekAndPutObject(objectStore objectstorev2.ObjectStore, bucket, key string, file io.Reader) error {
|
||||
func seekAndPutObject(objectStore velero.ObjectStore, bucket, key string, file io.Reader) error {
|
||||
if file == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ import (
|
||||
"github.com/vmware-tanzu/velero/internal/credentials"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks"
|
||||
objectstorev2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/objectstore/v2"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/encode"
|
||||
"github.com/vmware-tanzu/velero/pkg/volume"
|
||||
@@ -595,9 +595,9 @@ func TestGetDownloadURL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type objectStoreGetter map[string]objectstorev2.ObjectStore
|
||||
type objectStoreGetter map[string]velero.ObjectStore
|
||||
|
||||
func (osg objectStoreGetter) GetObjectStore(provider string) (objectstorev2.ObjectStore, error) {
|
||||
func (osg objectStoreGetter) GetObjectStore(provider string) (velero.ObjectStore, error) {
|
||||
res, ok := osg[provider]
|
||||
if !ok {
|
||||
return nil, errors.New("object store not found")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2021 the Velero contributors.
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -73,12 +73,6 @@ func (b *clientBuilder) clientConfig() *hcplugin.ClientConfig {
|
||||
string(framework.PluginKindPluginLister): &framework.PluginListerPlugin{},
|
||||
string(framework.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(framework.ClientLogger(b.clientLogger)),
|
||||
string(framework.PluginKindDeleteItemAction): framework.NewDeleteItemActionPlugin(framework.ClientLogger(b.clientLogger)),
|
||||
// Version 2
|
||||
string(framework.PluginKindBackupItemActionV2): framework.NewBackupItemActionPlugin(framework.ClientLogger(b.clientLogger)),
|
||||
string(framework.PluginKindVolumeSnapshotterV2): framework.NewVolumeSnapshotterPlugin(framework.ClientLogger(b.clientLogger)),
|
||||
string(framework.PluginKindObjectStoreV2): framework.NewObjectStorePlugin(framework.ClientLogger(b.clientLogger)),
|
||||
string(framework.PluginKindRestoreItemActionV2): framework.NewRestoreItemActionPlugin(framework.ClientLogger(b.clientLogger)),
|
||||
string(framework.PluginKindDeleteItemActionV2): framework.NewDeleteItemActionPlugin(framework.ClientLogger(b.clientLogger)),
|
||||
},
|
||||
Logger: b.pluginLogger,
|
||||
Cmd: exec.Command(b.commandName, b.commandArgs...),
|
||||
|
||||
@@ -18,7 +18,6 @@ package clientmgmt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
||||
hclog "github.com/hashicorp/go-hclog"
|
||||
@@ -163,37 +162,3 @@ func (l *logrusAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.L
|
||||
func (l *logrusAdapter) SetLevel(_ hclog.Level) {
|
||||
return
|
||||
}
|
||||
|
||||
// ImpliedArgs returns With key/value pairs
|
||||
func (l *logrusAdapter) ImpliedArgs() []interface{} {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// Args are alternating key, val pairs
|
||||
// keys must be strings
|
||||
// vals can be any type, but display is implementation specific
|
||||
// Emit a message and key/value pairs at a provided log level
|
||||
func (l *logrusAdapter) Log(level hclog.Level, msg string, args ...interface{}) {
|
||||
switch level {
|
||||
case hclog.Trace:
|
||||
l.Trace(msg, args...)
|
||||
case hclog.Debug:
|
||||
l.Debug(msg, args...)
|
||||
case hclog.Info:
|
||||
l.Info(msg, args...)
|
||||
case hclog.Warn:
|
||||
l.Warn(msg, args...)
|
||||
case hclog.Error:
|
||||
l.Error(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the Name of the logger
|
||||
func (l *logrusAdapter) Name() string {
|
||||
return l.name
|
||||
}
|
||||
|
||||
// Return a value that conforms to io.Writer, which can be passed into log.SetOutput()
|
||||
func (l *logrusAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2021 the Velero contributors.
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -17,46 +17,40 @@ limitations under the License.
|
||||
package clientmgmt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
|
||||
backupitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2"
|
||||
deleteitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/deleteitemaction/v2"
|
||||
objectstorev2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/objectstore/v2"
|
||||
restoreitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2"
|
||||
volumesnapshotterv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v2"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
)
|
||||
|
||||
// Manager manages the lifecycles of plugins.
|
||||
type Manager interface {
|
||||
// GetObjectStore returns the ObjectStore plugin for name.
|
||||
GetObjectStore(name string) (objectstorev2.ObjectStore, error)
|
||||
GetObjectStore(name string) (velero.ObjectStore, error)
|
||||
|
||||
// GetVolumeSnapshotter returns the VolumeSnapshotter plugin for name.
|
||||
GetVolumeSnapshotter(name string) (volumesnapshotterv2.VolumeSnapshotter, error)
|
||||
GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error)
|
||||
|
||||
// GetBackupItemActions returns all backup item action plugins.
|
||||
GetBackupItemActions() ([]backupitemactionv2.BackupItemAction, error)
|
||||
GetBackupItemActions() ([]velero.BackupItemAction, error)
|
||||
|
||||
// GetBackupItemAction returns the backup item action plugin for name.
|
||||
GetBackupItemAction(name string) (backupitemactionv2.BackupItemAction, error)
|
||||
GetBackupItemAction(name string) (velero.BackupItemAction, error)
|
||||
|
||||
// GetRestoreItemActions returns all restore item action plugins.
|
||||
GetRestoreItemActions() ([]restoreitemactionv2.RestoreItemAction, error)
|
||||
GetRestoreItemActions() ([]velero.RestoreItemAction, error)
|
||||
|
||||
// GetRestoreItemAction returns the restore item action plugin for name.
|
||||
GetRestoreItemAction(name string) (restoreitemactionv2.RestoreItemAction, error)
|
||||
GetRestoreItemAction(name string) (velero.RestoreItemAction, error)
|
||||
|
||||
// GetDeleteItemActions returns all delete item action plugins.
|
||||
GetDeleteItemActions() ([]deleteitemactionv2.DeleteItemAction, error)
|
||||
GetDeleteItemActions() ([]velero.DeleteItemAction, error)
|
||||
|
||||
// GetDeleteItemAction returns the delete item action plugin for name.
|
||||
GetDeleteItemAction(name string) (deleteitemactionv2.DeleteItemAction, error)
|
||||
GetDeleteItemAction(name string) (velero.DeleteItemAction, error)
|
||||
|
||||
// CleanupClients terminates all of the Manager's running plugin processes.
|
||||
CleanupClients()
|
||||
@@ -135,82 +129,39 @@ func (m *manager) getRestartableProcess(kind framework.PluginKind, name string)
|
||||
return restartableProcess, nil
|
||||
}
|
||||
|
||||
type RestartableObjectStore struct {
|
||||
kind framework.PluginKind
|
||||
// Get returns a restartable ObjectStore for the given name and process, wrapping if necessary
|
||||
Get func(name string, restartableProcess RestartableProcess) objectstorev2.ObjectStore
|
||||
}
|
||||
|
||||
func (m *manager) restartableObjectStores() []RestartableObjectStore {
|
||||
return []RestartableObjectStore{
|
||||
{
|
||||
kind: framework.PluginKindObjectStoreV2,
|
||||
Get: newRestartableObjectStoreV2,
|
||||
},
|
||||
{
|
||||
kind: framework.PluginKindObjectStore,
|
||||
Get: newAdaptedV1ObjectStore, // Adapt v1 plugin to v2
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetObjectStore returns a restartableObjectStore for name.
|
||||
func (m *manager) GetObjectStore(name string) (objectstorev2.ObjectStore, error) {
|
||||
func (m *manager) GetObjectStore(name string) (velero.ObjectStore, error) {
|
||||
name = sanitizeName(name)
|
||||
for _, restartableObjStore := range m.restartableObjectStores() {
|
||||
restartableProcess, err := m.getRestartableProcess(restartableObjStore.kind, name)
|
||||
if err != nil {
|
||||
// Check if plugin was not found
|
||||
if errors.Is(err, &pluginNotFoundError{}) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return restartableObjStore.Get(name, restartableProcess), nil
|
||||
}
|
||||
return nil, fmt.Errorf("unable to get valid ObjectStore for %q", name)
|
||||
}
|
||||
|
||||
type RestartableVolumeSnapshotter struct {
|
||||
kind framework.PluginKind
|
||||
// Get returns a restartable VolumeSnapshotter for the given name and process, wrapping if necessary
|
||||
Get func(name string, restartableProcess RestartableProcess) volumesnapshotterv2.VolumeSnapshotter
|
||||
}
|
||||
|
||||
func (m *manager) restartableVolumeSnapshotters() []RestartableVolumeSnapshotter {
|
||||
return []RestartableVolumeSnapshotter{
|
||||
{
|
||||
kind: framework.PluginKindVolumeSnapshotterV2,
|
||||
Get: newRestartableVolumeSnapshotterV2,
|
||||
},
|
||||
{
|
||||
kind: framework.PluginKindVolumeSnapshotter,
|
||||
Get: newAdaptedV1VolumeSnapshotter, // Adapt v1 plugin to v2
|
||||
},
|
||||
restartableProcess, err := m.getRestartableProcess(framework.PluginKindObjectStore, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := newRestartableObjectStore(name, restartableProcess)
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// GetVolumeSnapshotter returns a restartableVolumeSnapshotter for name.
|
||||
func (m *manager) GetVolumeSnapshotter(name string) (volumesnapshotterv2.VolumeSnapshotter, error) {
|
||||
func (m *manager) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) {
|
||||
name = sanitizeName(name)
|
||||
for _, restartableVolumeSnapshotter := range m.restartableVolumeSnapshotters() {
|
||||
restartableProcess, err := m.getRestartableProcess(restartableVolumeSnapshotter.kind, name)
|
||||
if err != nil {
|
||||
// Check if plugin was not found
|
||||
if errors.Is(err, &pluginNotFoundError{}) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return restartableVolumeSnapshotter.Get(name, restartableProcess), nil
|
||||
|
||||
restartableProcess, err := m.getRestartableProcess(framework.PluginKindVolumeSnapshotter, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, fmt.Errorf("unable to get valid VolumeSnapshotter for %q", name)
|
||||
|
||||
r := newRestartableVolumeSnapshotter(name, restartableProcess)
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// GetBackupItemActions returns all backup item actions as restartableBackupItemActions.
|
||||
func (m *manager) GetBackupItemActions() ([]backupitemactionv2.BackupItemAction, error) {
|
||||
list := m.registry.ListForKinds(framework.BackupItemActionKinds())
|
||||
actions := make([]backupitemactionv2.BackupItemAction, 0, len(list))
|
||||
func (m *manager) GetBackupItemActions() ([]velero.BackupItemAction, error) {
|
||||
list := m.registry.List(framework.PluginKindBackupItemAction)
|
||||
|
||||
actions := make([]velero.BackupItemAction, 0, len(list))
|
||||
|
||||
for i := range list {
|
||||
id := list[i]
|
||||
@@ -226,47 +177,24 @@ func (m *manager) GetBackupItemActions() ([]backupitemactionv2.BackupItemAction,
|
||||
return actions, nil
|
||||
}
|
||||
|
||||
type RestartableBackupItemAction struct {
|
||||
kind framework.PluginKind
|
||||
// Get returns a restartable BackupItemAction for the given name and process, wrapping if necessary
|
||||
Get func(name string, restartableProcess RestartableProcess) backupitemactionv2.BackupItemAction
|
||||
}
|
||||
|
||||
func (m *manager) restartableBackupItemActions() []RestartableBackupItemAction {
|
||||
return []RestartableBackupItemAction{
|
||||
{
|
||||
kind: framework.PluginKindBackupItemActionV2,
|
||||
Get: newRestartableBackupItemActionV2,
|
||||
},
|
||||
{
|
||||
kind: framework.PluginKindBackupItemAction,
|
||||
Get: newAdaptedV1BackupItemAction, // Adapt v1 plugin to v2
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetBackupItemAction returns a restartableBackupItemAction for name.
|
||||
func (m *manager) GetBackupItemAction(name string) (backupitemactionv2.BackupItemAction, error) {
|
||||
func (m *manager) GetBackupItemAction(name string) (velero.BackupItemAction, error) {
|
||||
name = sanitizeName(name)
|
||||
for _, restartableBackupItemAction := range m.restartableBackupItemActions() {
|
||||
restartableProcess, err := m.getRestartableProcess(restartableBackupItemAction.kind, name)
|
||||
if err != nil {
|
||||
// Check if plugin was not found
|
||||
if errors.Is(err, &pluginNotFoundError{}) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return restartableBackupItemAction.Get(name, restartableProcess), nil
|
||||
|
||||
restartableProcess, err := m.getRestartableProcess(framework.PluginKindBackupItemAction, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, fmt.Errorf("unable to get valid BackupItemAction for %q", name)
|
||||
|
||||
r := newRestartableBackupItemAction(name, restartableProcess)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// GetRestoreItemActions returns all restore item actions as restartableRestoreItemActions.
|
||||
func (m *manager) GetRestoreItemActions() ([]restoreitemactionv2.RestoreItemAction, error) {
|
||||
list := m.registry.ListForKinds(framework.RestoreItemActionKinds())
|
||||
func (m *manager) GetRestoreItemActions() ([]velero.RestoreItemAction, error) {
|
||||
list := m.registry.List(framework.PluginKindRestoreItemAction)
|
||||
|
||||
actions := make([]restoreitemactionv2.RestoreItemAction, 0, len(list))
|
||||
actions := make([]velero.RestoreItemAction, 0, len(list))
|
||||
|
||||
for i := range list {
|
||||
id := list[i]
|
||||
@@ -282,47 +210,24 @@ func (m *manager) GetRestoreItemActions() ([]restoreitemactionv2.RestoreItemActi
|
||||
return actions, nil
|
||||
}
|
||||
|
||||
type RestartableRestoreItemAction struct {
|
||||
kind framework.PluginKind
|
||||
// Get returns a restartable RestoreItemAction for the given name and process, wrapping if necessary
|
||||
Get func(name string, restartableProcess RestartableProcess) restoreitemactionv2.RestoreItemAction
|
||||
}
|
||||
|
||||
func (m *manager) restartableRestoreItemActions() []RestartableRestoreItemAction {
|
||||
return []RestartableRestoreItemAction{
|
||||
{
|
||||
kind: framework.PluginKindRestoreItemActionV2,
|
||||
Get: newRestartableRestoreItemActionV2,
|
||||
},
|
||||
{
|
||||
kind: framework.PluginKindRestoreItemAction,
|
||||
Get: newAdaptedV1RestoreItemAction, // Adapt v1 plugin to v2
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetRestoreItemAction returns a restartableRestoreItemAction for name.
|
||||
func (m *manager) GetRestoreItemAction(name string) (restoreitemactionv2.RestoreItemAction, error) {
|
||||
func (m *manager) GetRestoreItemAction(name string) (velero.RestoreItemAction, error) {
|
||||
name = sanitizeName(name)
|
||||
for _, restartableRestoreItemAction := range m.restartableRestoreItemActions() {
|
||||
restartableProcess, err := m.getRestartableProcess(restartableRestoreItemAction.kind, name)
|
||||
if err != nil {
|
||||
// Check if plugin was not found
|
||||
if errors.Is(err, &pluginNotFoundError{}) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return restartableRestoreItemAction.Get(name, restartableProcess), nil
|
||||
|
||||
restartableProcess, err := m.getRestartableProcess(framework.PluginKindRestoreItemAction, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, fmt.Errorf("unable to get valid RestoreItemAction for %q", name)
|
||||
|
||||
r := newRestartableRestoreItemAction(name, restartableProcess)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// GetDeleteItemActions returns all delete item actions as restartableDeleteItemActions.
|
||||
func (m *manager) GetDeleteItemActions() ([]deleteitemactionv2.DeleteItemAction, error) {
|
||||
list := m.registry.ListForKinds(framework.DeleteItemActionKinds())
|
||||
func (m *manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) {
|
||||
list := m.registry.List(framework.PluginKindDeleteItemAction)
|
||||
|
||||
actions := make([]deleteitemactionv2.DeleteItemAction, 0, len(list))
|
||||
actions := make([]velero.DeleteItemAction, 0, len(list))
|
||||
|
||||
for i := range list {
|
||||
id := list[i]
|
||||
@@ -338,40 +243,17 @@ func (m *manager) GetDeleteItemActions() ([]deleteitemactionv2.DeleteItemAction,
|
||||
return actions, nil
|
||||
}
|
||||
|
||||
type RestartableDeleteItemAction struct {
|
||||
kind framework.PluginKind
|
||||
// Get returns a restartable DeleteItemAction for the given name and process, wrapping if necessary
|
||||
Get func(name string, restartableProcess RestartableProcess) deleteitemactionv2.DeleteItemAction
|
||||
}
|
||||
|
||||
func (m *manager) restartableDeleteItemActions() []RestartableDeleteItemAction {
|
||||
return []RestartableDeleteItemAction{
|
||||
{
|
||||
kind: framework.PluginKindDeleteItemActionV2,
|
||||
Get: newRestartableDeleteItemActionV2,
|
||||
},
|
||||
{
|
||||
kind: framework.PluginKindDeleteItemAction,
|
||||
Get: newAdaptedV1DeleteItemAction, // Adapt v1 plugin to v2
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetDeleteItemAction returns a restartableDeleteItemAction for name.
|
||||
func (m *manager) GetDeleteItemAction(name string) (deleteitemactionv2.DeleteItemAction, error) {
|
||||
func (m *manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, error) {
|
||||
name = sanitizeName(name)
|
||||
for _, restartableDeleteItemAction := range m.restartableDeleteItemActions() {
|
||||
restartableProcess, err := m.getRestartableProcess(restartableDeleteItemAction.kind, name)
|
||||
if err != nil {
|
||||
// Check if plugin was not found
|
||||
if errors.Is(err, &pluginNotFoundError{}) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return restartableDeleteItemAction.Get(name, restartableProcess), nil
|
||||
|
||||
restartableProcess, err := m.getRestartableProcess(framework.PluginKindDeleteItemAction, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, fmt.Errorf("unable to get valid DeleteItemAction for %q", name)
|
||||
|
||||
r := newRestartableDeleteItemAction(name, restartableProcess)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// sanitizeName adds "velero.io" to legacy plugins that weren't namespaced.
|
||||
|
||||
@@ -34,8 +34,6 @@ type Registry interface {
|
||||
DiscoverPlugins() error
|
||||
// List returns all PluginIdentifiers for kind.
|
||||
List(kind framework.PluginKind) []framework.PluginIdentifier
|
||||
// List returns all PluginIdentifiers for a list of kinds.
|
||||
ListForKinds(kinds []framework.PluginKind) (list []framework.PluginIdentifier)
|
||||
// Get returns the PluginIdentifier for kind and name.
|
||||
Get(kind framework.PluginKind, name string) (framework.PluginIdentifier, error)
|
||||
}
|
||||
@@ -110,13 +108,6 @@ func (r *registry) discoverPlugins(commands []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *registry) ListForKinds(kinds []framework.PluginKind) (list []framework.PluginIdentifier) {
|
||||
for _, kind := range kinds {
|
||||
list = append(list, r.pluginsByKind[kind]...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// List returns info about all plugin binaries that implement the given
|
||||
// PluginKind.
|
||||
func (r *registry) List(kind framework.PluginKind) []framework.PluginIdentifier {
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 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 clientmgmt
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
backupitemactionv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1"
|
||||
backupitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2"
|
||||
)
|
||||
|
||||
type restartableAdaptedV1BackupItemAction struct {
|
||||
key kindAndName
|
||||
sharedPluginProcess RestartableProcess
|
||||
}
|
||||
|
||||
// newAdaptedV1BackupItemAction returns a new restartableAdaptedV1BackupItemAction.
|
||||
func newAdaptedV1BackupItemAction(
|
||||
name string, sharedPluginProcess RestartableProcess) backupitemactionv2.BackupItemAction {
|
||||
r := &restartableAdaptedV1BackupItemAction{
|
||||
key: kindAndName{kind: framework.PluginKindBackupItemAction, name: name},
|
||||
sharedPluginProcess: sharedPluginProcess,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// getBackupItemAction returns the backup item action for this restartableAdaptedV1BackupItemAction.
|
||||
// It does *not* restart the plugin process.
|
||||
func (r *restartableAdaptedV1BackupItemAction) getBackupItemAction() (backupitemactionv1.BackupItemAction, error) {
|
||||
plugin, err := r.sharedPluginProcess.getByKindAndName(r.key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
backupItemAction, ok := plugin.(backupitemactionv1.BackupItemAction)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("%T is not a BackupItemAction!", plugin)
|
||||
}
|
||||
|
||||
return backupItemAction, nil
|
||||
}
|
||||
|
||||
// getDelegate restarts the plugin process (if needed) and returns the backup item
|
||||
// action for this restartableAdaptedV1BackupItemAction.
|
||||
func (r *restartableAdaptedV1BackupItemAction) getDelegate() (backupitemactionv1.BackupItemAction, error) {
|
||||
if err := r.sharedPluginProcess.resetIfNeeded(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.getBackupItemAction()
|
||||
}
|
||||
|
||||
// AppliesTo restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1BackupItemAction) AppliesTo() (velero.ResourceSelector, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return velero.ResourceSelector{}, err
|
||||
}
|
||||
|
||||
return delegate.AppliesTo()
|
||||
}
|
||||
|
||||
// Execute restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1BackupItemAction) Execute(
|
||||
item runtime.Unstructured, backup *api.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return delegate.Execute(item, backup)
|
||||
}
|
||||
|
||||
// Version 2: simply discard ctx and call version 1 function.
|
||||
// ExecuteV2 restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1BackupItemAction) ExecuteV2(
|
||||
ctx context.Context, item runtime.Unstructured, backup *api.Backup) (
|
||||
runtime.Unstructured, []velero.ResourceIdentifier, error) {
|
||||
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return delegate.Execute(item, backup)
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 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 clientmgmt
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
deleteitemactionv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/deleteitemaction/v1"
|
||||
deleteitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/deleteitemaction/v2"
|
||||
)
|
||||
|
||||
type restartableAdaptedV1DeleteItemAction struct {
|
||||
key kindAndName
|
||||
sharedPluginProcess RestartableProcess
|
||||
config map[string]string
|
||||
}
|
||||
|
||||
// newAdaptedV1DeleteItemAction returns a new restartableAdaptedV1DeleteItemAction.
|
||||
func newAdaptedV1DeleteItemAction(
|
||||
name string, sharedPluginProcess RestartableProcess) deleteitemactionv2.DeleteItemAction {
|
||||
r := &restartableAdaptedV1DeleteItemAction{
|
||||
key: kindAndName{kind: framework.PluginKindDeleteItemAction, name: name},
|
||||
sharedPluginProcess: sharedPluginProcess,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// getDeleteItemAction returns the delete item action for this restartableDeleteItemAction.
|
||||
// It does *not* restart the plugin process.
|
||||
func (r *restartableAdaptedV1DeleteItemAction) getDeleteItemAction() (deleteitemactionv1.DeleteItemAction, error) {
|
||||
plugin, err := r.sharedPluginProcess.getByKindAndName(r.key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deleteItemAction, ok := plugin.(deleteitemactionv1.DeleteItemAction)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("%T is not a DeleteItemAction!", plugin)
|
||||
}
|
||||
|
||||
return deleteItemAction, nil
|
||||
}
|
||||
|
||||
// getDelegate restarts the plugin process (if needed) and returns the delete item action for this restartableDeleteItemAction.
|
||||
func (r *restartableAdaptedV1DeleteItemAction) getDelegate() (deleteitemactionv1.DeleteItemAction, error) {
|
||||
if err := r.sharedPluginProcess.resetIfNeeded(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.getDeleteItemAction()
|
||||
}
|
||||
|
||||
// AppliesTo restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1DeleteItemAction) AppliesTo() (velero.ResourceSelector, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return velero.ResourceSelector{}, err
|
||||
}
|
||||
|
||||
return delegate.AppliesTo()
|
||||
}
|
||||
|
||||
// Execute restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1DeleteItemAction) Execute(input *velero.DeleteItemActionExecuteInput) error {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return delegate.Execute(input)
|
||||
}
|
||||
|
||||
// ExecuteV2 restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1DeleteItemAction) ExecuteV2(
|
||||
ctx context.Context, input *velero.DeleteItemActionExecuteInput) error {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return delegate.Execute(input)
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 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 clientmgmt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
|
||||
objectstorev1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/objectstore/v1"
|
||||
objectstorev2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/objectstore/v2"
|
||||
)
|
||||
|
||||
// restartableAdaptedV1ObjectStore is restartableAdaptedV1ObjectStore version 1 adaptive to version 2 plugin
|
||||
type restartableAdaptedV1ObjectStore struct {
|
||||
restartableObjectStore
|
||||
}
|
||||
|
||||
// newAdaptedV1ObjectStore returns a new restartableAdaptedV1ObjectStore.
|
||||
func newAdaptedV1ObjectStore(name string, sharedPluginProcess RestartableProcess) objectstorev2.ObjectStore {
|
||||
key := kindAndName{kind: framework.PluginKindObjectStore, name: name}
|
||||
r := &restartableAdaptedV1ObjectStore{
|
||||
restartableObjectStore: restartableObjectStore{
|
||||
key: key,
|
||||
sharedPluginProcess: sharedPluginProcess,
|
||||
},
|
||||
}
|
||||
|
||||
// Register our reinitializer so we can reinitialize after a restart with r.config.
|
||||
sharedPluginProcess.addReinitializer(key, r)
|
||||
return r
|
||||
}
|
||||
|
||||
// reinitialize reinitializes a re-dispensed plugin using the initial data passed to Init().
|
||||
func (r *restartableAdaptedV1ObjectStore) reinitialize(dispensed interface{}) error {
|
||||
objectStore, ok := dispensed.(objectstorev1.ObjectStore)
|
||||
if !ok {
|
||||
return errors.Errorf("%T is not a ObjectStore!", dispensed)
|
||||
}
|
||||
|
||||
return r.init(objectStore, r.config)
|
||||
}
|
||||
|
||||
// getObjectStore returns the object store for this restartableObjectStore.
|
||||
// It does *not* restart the plugin process.
|
||||
func (r *restartableAdaptedV1ObjectStore) getObjectStore() (objectstorev1.ObjectStore, error) {
|
||||
plugin, err := r.sharedPluginProcess.getByKindAndName(r.key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
objectStore, ok := plugin.(objectstorev1.ObjectStore)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("%T is not a ObjectStore!", plugin)
|
||||
}
|
||||
|
||||
return objectStore, nil
|
||||
}
|
||||
|
||||
// getDelegate restarts the plugin process (if needed) and returns the object store for this restartableObjectStore.
|
||||
func (r *restartableAdaptedV1ObjectStore) getDelegate() (objectstorev1.ObjectStore, error) {
|
||||
if err := r.sharedPluginProcess.resetIfNeeded(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.getObjectStore()
|
||||
}
|
||||
|
||||
// Init initializes the object store instance using config. If this is the first invocation, r stores config for future
|
||||
// reinitialization needs. Init does NOT restart the shared plugin process. Init may only be called once.
|
||||
func (r *restartableAdaptedV1ObjectStore) Init(config map[string]string) error {
|
||||
if r.config != nil {
|
||||
return errors.Errorf("already initialized")
|
||||
}
|
||||
|
||||
// Not using getDelegate() to avoid possible infinite recursion
|
||||
delegate, err := r.getObjectStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.config = config
|
||||
|
||||
return r.init(delegate, config)
|
||||
}
|
||||
|
||||
func (r *restartableAdaptedV1ObjectStore) InitV2(ctx context.Context, config map[string]string) error {
|
||||
return r.Init(config)
|
||||
}
|
||||
|
||||
// init calls Init on objectStore with config. This is split out from Init() so that both Init() and reinitialize() may
|
||||
// call it using a specific ObjectStore.
|
||||
func (r *restartableAdaptedV1ObjectStore) init(objectStore objectstorev1.ObjectStore, config map[string]string) error {
|
||||
return objectStore.Init(config)
|
||||
}
|
||||
|
||||
// PutObject restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) PutObject(bucket string, key string, body io.Reader) error {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return delegate.PutObject(bucket, key, body)
|
||||
}
|
||||
|
||||
// ObjectExists restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) ObjectExists(bucket, key string) (bool, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return delegate.ObjectExists(bucket, key)
|
||||
}
|
||||
|
||||
// GetObject restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) GetObject(bucket string, key string) (io.ReadCloser, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delegate.GetObject(bucket, key)
|
||||
}
|
||||
|
||||
// ListCommonPrefixes restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) ListCommonPrefixes(
|
||||
bucket string, prefix string, delimiter string) ([]string, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delegate.ListCommonPrefixes(bucket, prefix, delimiter)
|
||||
}
|
||||
|
||||
// ListObjects restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) ListObjects(bucket string, prefix string) ([]string, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delegate.ListObjects(bucket, prefix)
|
||||
}
|
||||
|
||||
// DeleteObject restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) DeleteObject(bucket string, key string) error {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return delegate.DeleteObject(bucket, key)
|
||||
}
|
||||
|
||||
// CreateSignedURL restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) CreateSignedURL(
|
||||
bucket string, key string, ttl time.Duration) (string, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return delegate.CreateSignedURL(bucket, key, ttl)
|
||||
}
|
||||
|
||||
// Version 2. Simply discard ctx.
|
||||
// PutObjectV2 restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) PutObjectV2(
|
||||
ctx context.Context, bucket string, key string, body io.Reader) error {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return delegate.PutObject(bucket, key, body)
|
||||
}
|
||||
|
||||
// ObjectExistsV2 restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) ObjectExistsV2(ctx context.Context, bucket, key string) (bool, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return delegate.ObjectExists(bucket, key)
|
||||
}
|
||||
|
||||
// GetObjectV2 restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) GetObjectV2(
|
||||
ctx context.Context, bucket string, key string) (io.ReadCloser, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delegate.GetObject(bucket, key)
|
||||
}
|
||||
|
||||
// ListCommonPrefixesV2 restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) ListCommonPrefixesV2(
|
||||
ctx context.Context, bucket string, prefix string, delimiter string) ([]string, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delegate.ListCommonPrefixes(bucket, prefix, delimiter)
|
||||
}
|
||||
|
||||
// ListObjectsV2 restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) ListObjectsV2(
|
||||
ctx context.Context, bucket string, prefix string) ([]string, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delegate.ListObjects(bucket, prefix)
|
||||
}
|
||||
|
||||
// DeleteObjectV2 restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) DeleteObjectV2(ctx context.Context, bucket string, key string) error {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return delegate.DeleteObject(bucket, key)
|
||||
}
|
||||
|
||||
// CreateSignedURLV2 restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1ObjectStore) CreateSignedURLV2(
|
||||
ctx context.Context, bucket string, key string, ttl time.Duration) (string, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return delegate.CreateSignedURL(bucket, key, ttl)
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 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 clientmgmt
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
restoreitemactionv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1"
|
||||
restoreitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2"
|
||||
)
|
||||
|
||||
type restartableAdaptedV1RestoreItemAction struct {
|
||||
key kindAndName
|
||||
sharedPluginProcess RestartableProcess
|
||||
config map[string]string
|
||||
}
|
||||
|
||||
// newRestartableRestoreItemAction returns a new restartableRestoreItemAction.
|
||||
func newAdaptedV1RestoreItemAction(
|
||||
name string, sharedPluginProcess RestartableProcess) restoreitemactionv2.RestoreItemAction {
|
||||
r := &restartableAdaptedV1RestoreItemAction{
|
||||
key: kindAndName{kind: framework.PluginKindRestoreItemAction, name: name},
|
||||
sharedPluginProcess: sharedPluginProcess,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// getRestoreItemAction returns the restore item action for this restartableRestoreItemAction.
|
||||
// It does *not* restart the plugin process.
|
||||
func (r *restartableAdaptedV1RestoreItemAction) getRestoreItemAction() (restoreitemactionv1.RestoreItemAction, error) {
|
||||
plugin, err := r.sharedPluginProcess.getByKindAndName(r.key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
restoreItemAction, ok := plugin.(restoreitemactionv1.RestoreItemAction)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("%T is not a RestoreItemAction!", plugin)
|
||||
}
|
||||
|
||||
return restoreItemAction, nil
|
||||
}
|
||||
|
||||
// getDelegate restarts the plugin process (if needed) and returns the restore item action for this restartableRestoreItemAction.
|
||||
func (r *restartableAdaptedV1RestoreItemAction) getDelegate() (restoreitemactionv1.RestoreItemAction, error) {
|
||||
if err := r.sharedPluginProcess.resetIfNeeded(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.getRestoreItemAction()
|
||||
}
|
||||
|
||||
// AppliesTo restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1RestoreItemAction) AppliesTo() (velero.ResourceSelector, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return velero.ResourceSelector{}, err
|
||||
}
|
||||
|
||||
return delegate.AppliesTo()
|
||||
}
|
||||
|
||||
// Execute restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1RestoreItemAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return delegate.Execute(input)
|
||||
}
|
||||
|
||||
// ExecuteV2 restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableAdaptedV1RestoreItemAction) ExecuteV2(
|
||||
ctx context.Context, input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
|
||||
delegate, err := r.getDelegate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return delegate.Execute(input)
|
||||
}
|
||||