mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-03-27 03:55:04 +00:00
Compare commits
197 Commits
v1.14.1
...
v1.12.4-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d8417b2c5 | ||
|
|
26ca08b1f5 | ||
|
|
dd5f74d53e | ||
|
|
dcdc32f16f | ||
|
|
5b07cbb97f | ||
|
|
a3bb4d2947 | ||
|
|
d71b298ceb | ||
|
|
9e35ecd47a | ||
|
|
341d8006af | ||
|
|
e0b437e2e1 | ||
|
|
5f32403968 | ||
|
|
4d1c20bbab | ||
|
|
3c379edcd0 | ||
|
|
7295609bd6 | ||
|
|
21667a14b4 | ||
|
|
bd0ea3a648 | ||
|
|
509ceaafbf | ||
|
|
cd9b5ed37b | ||
|
|
1cc6f74341 | ||
|
|
50a12217e4 | ||
|
|
85189a0dc6 | ||
|
|
c3aecbb761 | ||
|
|
6886ff6643 | ||
|
|
165cd5e908 | ||
|
|
ce00387ccf | ||
|
|
684f71306e | ||
|
|
805ec1d3a6 | ||
|
|
4d544ac3bb | ||
|
|
5a178b88d1 | ||
|
|
03bad256b0 | ||
|
|
cf7074f943 | ||
|
|
0722927df2 | ||
|
|
106e802f1e | ||
|
|
9a093a7b75 | ||
|
|
09ff5af208 | ||
|
|
1da7f30fba | ||
|
|
dd2553916e | ||
|
|
6ff9dfab33 | ||
|
|
c3b5b1fadd | ||
|
|
568e5bf04a | ||
|
|
5ff8a4f2a3 | ||
|
|
33e3c9589b | ||
|
|
ee7b60f723 | ||
|
|
ecb8e80f30 | ||
|
|
a80981524b | ||
|
|
5e71b38993 | ||
|
|
06d12dec40 | ||
|
|
b68486221d | ||
|
|
5abd318b2c | ||
|
|
7c051514fd | ||
|
|
c8fd9d4d62 | ||
|
|
ccfbcc5455 | ||
|
|
ea25b8a793 | ||
|
|
2d6578635d | ||
|
|
fc44f3b8f0 | ||
|
|
df72745909 | ||
|
|
453bd93c90 | ||
|
|
65939c920e | ||
|
|
c042d477ab | ||
|
|
5c44ed49a5 | ||
|
|
3325a0cd1b | ||
|
|
b2d3fa0bec | ||
|
|
25fc2f4d6e | ||
|
|
a036e8d463 | ||
|
|
f92cdb1f76 | ||
|
|
0531dbb1a2 | ||
|
|
de55794381 | ||
|
|
d7b4b0a770 | ||
|
|
f1c93bd6c4 | ||
|
|
06e3773b22 | ||
|
|
32a8bbb9ac | ||
|
|
84d8bbda24 | ||
|
|
86e34eec28 | ||
|
|
5923046471 | ||
|
|
d1399225da | ||
|
|
88732e9b51 | ||
|
|
dd9de76381 | ||
|
|
6438fc9a69 | ||
|
|
a674a1eaff | ||
|
|
bb4f9094fd | ||
|
|
1264c438c1 | ||
|
|
7e35fd3261 | ||
|
|
482ec13d38 | ||
|
|
dd825ef8bb | ||
|
|
dc525aa045 | ||
|
|
36ad5dafa9 | ||
|
|
7b76047596 | ||
|
|
f1fcec3514 | ||
|
|
17ad487803 | ||
|
|
bb6c1f60ea | ||
|
|
0be6ad3a06 | ||
|
|
1c462d5f6d | ||
|
|
32deef7ae3 | ||
|
|
72b5e7aad6 | ||
|
|
5c4fdfe147 | ||
|
|
226237bab4 | ||
|
|
bbc9790316 | ||
|
|
10744ec516 | ||
|
|
905cd43140 | ||
|
|
3034cdb448 | ||
|
|
353ff55e42 | ||
|
|
468017d7db | ||
|
|
6bf705fd25 | ||
|
|
7a909d8ff5 | ||
|
|
ef1b9816b2 | ||
|
|
457fcc6893 | ||
|
|
b498847b5b | ||
|
|
af9697814e | ||
|
|
d92a051795 | ||
|
|
a3cb39d62e | ||
|
|
c1ace31466 | ||
|
|
8bf98e8895 | ||
|
|
e53cfdf85e | ||
|
|
d93cc9094a | ||
|
|
15dd67e203 | ||
|
|
877592194b | ||
|
|
17b495fcfd | ||
|
|
b99a59480d | ||
|
|
a789976a03 | ||
|
|
52878de077 | ||
|
|
432a5fe566 | ||
|
|
175047baa9 | ||
|
|
0eaf14ed19 | ||
|
|
c415fd4bcc | ||
|
|
554403df5c | ||
|
|
aba64ba151 | ||
|
|
3a410c9f04 | ||
|
|
2f92f78be5 | ||
|
|
9d5dd8e09d | ||
|
|
6103073551 | ||
|
|
83f892d81f | ||
|
|
2cd15f1e4b | ||
|
|
27a89df34d | ||
|
|
e4c2b2b157 | ||
|
|
edefe7a63b | ||
|
|
a097094bcf | ||
|
|
bc4dc6c0c8 | ||
|
|
343e54f1b8 | ||
|
|
08d44b02a8 | ||
|
|
a8c76a4a00 | ||
|
|
0623ac363a | ||
|
|
1aea12a80c | ||
|
|
7112c62e49 | ||
|
|
dcb891a307 | ||
|
|
21353f00a8 | ||
|
|
5e7114899b | ||
|
|
b035680ce6 | ||
|
|
9eb133e635 | ||
|
|
6f1262d4c6 | ||
|
|
48e3278c6c | ||
|
|
acfc6e474f | ||
|
|
993d2c775f | ||
|
|
b70b01cde9 | ||
|
|
8b8a5a2bcc | ||
|
|
5b36cd7e83 | ||
|
|
3240fb196c | ||
|
|
d9859d99ba | ||
|
|
18d4fe45e8 | ||
|
|
60d5bb22f7 | ||
|
|
9468b8cfa9 | ||
|
|
420562111b | ||
|
|
cf0b2e9139 | ||
|
|
506415e60c | ||
|
|
3733a40637 | ||
|
|
fe1ade0226 | ||
|
|
86e1a74937 | ||
|
|
6260a44e62 | ||
|
|
06d9bfae8d | ||
|
|
4d1617470f | ||
|
|
1b2c82c9eb | ||
|
|
040060082a | ||
|
|
fc653bdfbe | ||
|
|
6790a18814 | ||
|
|
93995bfd00 | ||
|
|
80572934dc | ||
|
|
41d9b67945 | ||
|
|
a06107ac70 | ||
|
|
40a94e39ad | ||
|
|
7ea0d434d6 | ||
|
|
6b884ecc39 | ||
|
|
183f7ac154 | ||
|
|
75bda412a1 | ||
|
|
a2eb10df8f | ||
|
|
90bc1abd21 | ||
|
|
45165503ba | ||
|
|
53530130a5 | ||
|
|
ed256d74dd | ||
|
|
ab28a09a07 | ||
|
|
90f4cc5497 | ||
|
|
f505ed709b | ||
|
|
28074e3f37 | ||
|
|
240f33c09d | ||
|
|
fd08848471 | ||
|
|
5f585be24b | ||
|
|
5480acf0a0 | ||
|
|
e2d3e84bab | ||
|
|
0c0ccf949b |
1
.github/auto-assignees.yml
vendored
1
.github/auto-assignees.yml
vendored
@@ -16,7 +16,6 @@ reviewers:
|
||||
- qiuming-best
|
||||
- shubham-pampattiwar
|
||||
- Lyndon-Li
|
||||
- anshulahuja98
|
||||
|
||||
tech-writer:
|
||||
- sseago
|
||||
|
||||
9
.github/dependabot.yml
vendored
9
.github/dependabot.yml
vendored
@@ -1,14 +1,5 @@
|
||||
version: 2
|
||||
updates:
|
||||
# Dependencies listed in .github/workflows
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "Dependencies"
|
||||
- "github_actions"
|
||||
- "kind/changelog-not-required"
|
||||
# Dependencies listed in go.mod
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/" # Location of package manifests
|
||||
|
||||
33
.github/labeler.yml
vendored
33
.github/labeler.yml
vendored
@@ -1,33 +0,0 @@
|
||||
# This file is used by Auto Label PRs action.
|
||||
# Works with https://github.com/actions/labeler/
|
||||
# Below this line, the keys are labels to be applied, and the values are the file globs to match against.
|
||||
# Anything in the `design` directory gets the `Design` label.
|
||||
Area/Design:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: design/*
|
||||
# Anything that has plugin infra will be labeled.
|
||||
# Individual plugins don't necessarily live here, though
|
||||
Area/Plugins:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: pkg/plugins/**/*
|
||||
Dependencies:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: go.mod
|
||||
Documentation:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: site/content/docs/**/*
|
||||
# Anything in the site directory gets the website label *EXCEPT* docs
|
||||
Website:
|
||||
- all:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: site/**/*
|
||||
- all-globs-to-all-files: '!site/content/docs/**/*'
|
||||
has-changelog:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: changelogs/**
|
||||
has-e2e-2tests:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: test/e2e/**/*
|
||||
has-unit-tests:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: pkg/**/*_test.go
|
||||
43
.github/labels.yaml
vendored
43
.github/labels.yaml
vendored
@@ -1,43 +0,0 @@
|
||||
# This file is used by [prow github action](https://github.com/jpmcb/prow-github-actions/) in .github/workflows/prow-action.yml.
|
||||
# This file only has values for kind and area commands.
|
||||
area:
|
||||
- CLI
|
||||
- CSI
|
||||
- Cloud/AWS
|
||||
- Cloud/Azure
|
||||
- Cloud/DigitalOcean
|
||||
- Cloud/GCP
|
||||
- Cloud/vSphere
|
||||
- Design
|
||||
- Documentation
|
||||
- Filters
|
||||
- Plugins
|
||||
- Process
|
||||
- Storage/Minio
|
||||
- Storage/Cinder
|
||||
- WindowsSupport
|
||||
- datamover
|
||||
- fs-backup
|
||||
- fs-backup/deletion
|
||||
- fs-backup/file-selectable
|
||||
- fs-uploader
|
||||
- kopia-integration
|
||||
- migration
|
||||
- multi-tenancy
|
||||
- progress-monitoring
|
||||
- resilience
|
||||
- schedule
|
||||
- storage/IBM-ObjectStorage
|
||||
- upgrade
|
||||
- volume-snapshot-dm
|
||||
kind:
|
||||
- changelog-not-required
|
||||
- question
|
||||
- refactor
|
||||
- requirement
|
||||
- release-note
|
||||
- release-blocker
|
||||
- spike
|
||||
- tech-debt
|
||||
- usage-error
|
||||
- voting
|
||||
41
.github/labels.yml
vendored
Normal file
41
.github/labels.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
area:
|
||||
- "Cloud/AWS"
|
||||
- "Cloud/GCP"
|
||||
- "Cloud/Azure"
|
||||
- "Design"
|
||||
- "Plugins"
|
||||
|
||||
# Labels that can be applied to PRs with the /kind command
|
||||
kind:
|
||||
- "changelog-not-required"
|
||||
- "tech-debt"
|
||||
|
||||
# Works with https://github.com/actions/labeler/
|
||||
# Below this line, the keys are labels to be applied, and the values are the file globs to match against.
|
||||
# Anything in the `design` directory gets the `Design` label.
|
||||
Area/Design:
|
||||
- design/*
|
||||
|
||||
# Anything in the site directory gets the website label *EXCEPT* docs
|
||||
Website:
|
||||
- any: ["site/**/*", "!site/content/docs/**/*"]
|
||||
|
||||
Documentation:
|
||||
- site/content/docs/**/*
|
||||
|
||||
Dependencies:
|
||||
- go.mod
|
||||
|
||||
# Anything that has plugin infra will be labeled.
|
||||
# Individual plugins don't necessarily live here, though
|
||||
Area/Plugins:
|
||||
- "pkg/plugins/**/*"
|
||||
|
||||
has-unit-tests:
|
||||
- "pkg/**/*_test.go"
|
||||
|
||||
has-e2e-2tests:
|
||||
- "test/e2e/**/*"
|
||||
|
||||
has-changelog:
|
||||
- "changelogs/**"
|
||||
2
.github/workflows/auto_assign_prs.yml
vendored
2
.github/workflows/auto_assign_prs.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set the author of a PR as the assignee
|
||||
uses: kentaro-m/auto-assign-action@v2.0.0
|
||||
uses: kentaro-m/auto-assign-action@v1.1.1
|
||||
with:
|
||||
configuration-path: ".github/auto-assignees.yml"
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
4
.github/workflows/auto_label_prs.yml
vendored
4
.github/workflows/auto_label_prs.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
||||
- uses: actions/labeler@v3
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
configuration-path: .github/labeler.yml
|
||||
configuration-path: .github/labels.yml
|
||||
|
||||
2
.github/workflows/auto_request_review.yml
vendored
2
.github/workflows/auto_request_review.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
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.13.0
|
||||
uses: necojackarc/auto-request-review@v0.7.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
config: .github/auto-assignees.yml
|
||||
|
||||
28
.github/workflows/crds-verify-kind.yaml
vendored
28
.github/workflows/crds-verify-kind.yaml
vendored
@@ -12,14 +12,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.22.6'
|
||||
go-version: '1.20.10'
|
||||
id: go
|
||||
# Look for a CLI that's made for this PR
|
||||
- name: Fetch built CLI
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-velero-cli
|
||||
with:
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
velero-${{ github.event.pull_request.number }}-
|
||||
|
||||
- name: Fetch cached go modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v2
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
|
||||
# If no binaries were built for this PR, build it now.
|
||||
@@ -57,19 +57,19 @@ jobs:
|
||||
matrix:
|
||||
# Latest k8s versions. There's no series-based tag, nor is there a latest tag.
|
||||
k8s:
|
||||
- 1.23.17
|
||||
- 1.24.17
|
||||
- 1.25.16
|
||||
- 1.26.13
|
||||
- 1.27.10
|
||||
- 1.28.6
|
||||
- 1.29.1
|
||||
- 1.19.7
|
||||
- 1.20.2
|
||||
- 1.21.1
|
||||
- 1.22.0
|
||||
- 1.23.6
|
||||
- 1.24.2
|
||||
- 1.25.3
|
||||
# All steps run in parallel unless otherwise specified.
|
||||
# See https://docs.github.com/en/actions/learn-github-actions/managing-complex-workflows#creating-dependent-jobs
|
||||
steps:
|
||||
- name: Fetch built CLI
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-velero-cli
|
||||
with:
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
velero-${{ github.event.pull_request.number }}-
|
||||
- uses: engineerd/setup-kind@v0.5.0
|
||||
with:
|
||||
version: "v0.21.0"
|
||||
version: "v0.17.0"
|
||||
image: "kindest/node:v${{ matrix.k8s }}"
|
||||
- name: Install CRDs
|
||||
run: |
|
||||
|
||||
57
.github/workflows/e2e-test-kind.yaml
vendored
57
.github/workflows/e2e-test-kind.yaml
vendored
@@ -12,27 +12,27 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.22.6'
|
||||
go-version: '1.20.10'
|
||||
id: go
|
||||
# Look for a CLI that's made for this PR
|
||||
- name: Fetch built CLI
|
||||
id: cli-cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ./_output/bin/linux/amd64/velero
|
||||
# The cache key a combination of the current PR number and the commit SHA
|
||||
key: velero-cli-${{ github.event.pull_request.number }}-${{ github.sha }}
|
||||
- name: Fetch built image
|
||||
id: image-cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ./velero.tar
|
||||
# The cache key a combination of the current PR number and the commit SHA
|
||||
key: velero-image-${{ github.event.pull_request.number }}-${{ github.sha }}
|
||||
- name: Fetch cached go modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v2
|
||||
if: steps.cli-cache.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
if: steps.cli-cache.outputs.cache-hit != 'true' || steps.image-cache.outputs.cache-hit != 'true'
|
||||
# If no binaries were built for this PR, build it now.
|
||||
- name: Build Velero CLI
|
||||
@@ -60,48 +60,38 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
k8s:
|
||||
- 1.23.17
|
||||
- 1.24.17
|
||||
- 1.25.16
|
||||
- 1.26.13
|
||||
- 1.27.10
|
||||
- 1.28.6
|
||||
- 1.29.1
|
||||
focus:
|
||||
# tests to focus on, use `|` to concatenate multiple regexes to run on the same job
|
||||
# ordered according to e2e_suite_test.go order
|
||||
- Basic\]\[ClusterResource
|
||||
- ResourceFiltering
|
||||
- ResourceModifier|Backups|PrivilegesMgmt\]\[SSR
|
||||
- Schedule\]\[OrderedResources
|
||||
- NamespaceMapping\]\[Single\]\[Restic|NamespaceMapping\]\[Multiple\]\[Restic
|
||||
- Basic\]\[Nodeport
|
||||
- Basic\]\[StorageClass
|
||||
- 1.19.16
|
||||
- 1.20.15
|
||||
- 1.21.12
|
||||
- 1.22.9
|
||||
- 1.23.6
|
||||
- 1.24.0
|
||||
- 1.25.3
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.22.6'
|
||||
go-version: '1.20.10'
|
||||
id: go
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
- name: Install MinIO
|
||||
run:
|
||||
docker run -d --rm -p 9000:9000 -e "MINIO_ACCESS_KEY=minio" -e "MINIO_SECRET_KEY=minio123" -e "MINIO_DEFAULT_BUCKETS=bucket,additional-bucket" bitnami/minio:2021.6.17-debian-10-r7
|
||||
- uses: engineerd/setup-kind@v0.5.0
|
||||
with:
|
||||
version: "v0.21.0"
|
||||
version: "v0.17.0"
|
||||
image: "kindest/node:v${{ matrix.k8s }}"
|
||||
- name: Fetch built CLI
|
||||
id: cli-cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ./_output/bin/linux/amd64/velero
|
||||
key: velero-cli-${{ github.event.pull_request.number }}-${{ github.sha }}
|
||||
- name: Fetch built Image
|
||||
id: image-cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ./velero.tar
|
||||
key: velero-image-${{ github.event.pull_request.number }}-${{ github.sha }}
|
||||
@@ -110,7 +100,7 @@ jobs:
|
||||
kind load image-archive velero.tar
|
||||
# always try to fetch the cached go modules as the e2e test needs it either
|
||||
- name: Fetch cached go modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
@@ -133,13 +123,12 @@ jobs:
|
||||
CREDS_FILE=/tmp/credential BSL_BUCKET=bucket \
|
||||
ADDITIONAL_OBJECT_STORE_PROVIDER=aws ADDITIONAL_BSL_CONFIG=region=minio,s3ForcePathStyle="true",s3Url=http://$(hostname -i):9000 \
|
||||
ADDITIONAL_CREDS_FILE=/tmp/credential ADDITIONAL_BSL_BUCKET=additional-bucket \
|
||||
GINKGO_FOCUS='${{ matrix.focus }}' VELERO_IMAGE=velero:pr-test \
|
||||
GINKGO_SKIP='SKIP_KIND|pv-backup|Restic|Snapshot|LongTime' \
|
||||
make -C test/ run-e2e
|
||||
GINKGO_FOCUS='Basic\]\[ClusterResource' VELERO_IMAGE=velero:pr-test \
|
||||
make -C test/e2e run
|
||||
timeout-minutes: 30
|
||||
- name: Upload debug bundle
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: DebugBundle
|
||||
path: /home/runner/work/velero/velero/test/e2e/debug-bundle*
|
||||
4
.github/workflows/nightly-trivy-scan.yml
vendored
4
.github/workflows/nightly-trivy-scan.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
@@ -31,6 +31,6 @@ jobs:
|
||||
output: 'trivy-results.sarif'
|
||||
|
||||
- name: Upload Trivy scan results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
2
.github/workflows/pr-changelog-check.yml
vendored
2
.github/workflows/pr-changelog-check.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Changelog check
|
||||
if: ${{ !(contains(github.event.pull_request.labels.*.name, 'kind/changelog-not-required') || contains(github.event.pull_request.labels.*.name, 'Design') || contains(github.event.pull_request.labels.*.name, 'Website') || contains(github.event.pull_request.labels.*.name, 'Documentation'))}}
|
||||
|
||||
10
.github/workflows/pr-ci-check.yml
vendored
10
.github/workflows/pr-ci-check.yml
vendored
@@ -8,14 +8,14 @@ jobs:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.22.6'
|
||||
go-version: '1.20.10'
|
||||
id: go
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
- name: Fetch cached go modules
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
- name: Make ci
|
||||
run: make ci
|
||||
- name: Upload test coverage
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: coverage.out
|
||||
|
||||
4
.github/workflows/pr-codespell.yml
vendored
4
.github/workflows/pr-codespell.yml
vendored
@@ -8,14 +8,14 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Codespell
|
||||
uses: codespell-project/actions-codespell@master
|
||||
with:
|
||||
# ignore the config/.../crd.go file as it's generated binary data that is edited elswhere.
|
||||
skip: .git,*.png,*.jpg,*.woff,*.ttf,*.gif,*.ico,./config/crd/v1beta1/crds/crds.go,./config/crd/v1/crds/crds.go,./config/crd/v2alpha1/crds/crds.go,./go.sum,./LICENSE
|
||||
ignore_words_list: iam,aks,ist,bridget,ue,shouldnot,atleast,notin,sme
|
||||
ignore_words_list: iam,aks,ist,bridget,ue,shouldnot,atleast
|
||||
check_filenames: true
|
||||
check_hidden: true
|
||||
|
||||
|
||||
6
.github/workflows/pr-containers.yml
vendored
6
.github/workflows/pr-containers.yml
vendored
@@ -13,18 +13,18 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
name: Checkout
|
||||
|
||||
- name: Set up QEMU
|
||||
id: qemu
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
platforms: all
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
|
||||
2
.github/workflows/pr-goreleaser.yml
vendored
2
.github/workflows/pr-goreleaser.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
name: Checkout
|
||||
|
||||
- name: Verify .goreleaser.yml and try a dryrun release.
|
||||
|
||||
18
.github/workflows/pr-linter-check.yml
vendored
18
.github/workflows/pr-linter-check.yml
vendored
@@ -6,15 +6,9 @@ jobs:
|
||||
name: Run Linter Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
id: go
|
||||
- name: Linter check
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: v1.57.2
|
||||
args: --verbose
|
||||
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Linter check
|
||||
run: make lint
|
||||
|
||||
19
.github/workflows/prow-action.yml
vendored
19
.github/workflows/prow-action.yml
vendored
@@ -9,21 +9,12 @@ jobs:
|
||||
execute:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: jpmcb/prow-github-actions@v1.1.3
|
||||
- uses: jpmcb/prow-github-actions@v1.1.2
|
||||
with:
|
||||
# Only support /kind command for now.
|
||||
# TODO: before allowing the /lgtm command, see if we can block merging if changelog labels are missing.
|
||||
prow-commands: |
|
||||
/approve
|
||||
/area
|
||||
/assign
|
||||
/cc
|
||||
/close
|
||||
/hold
|
||||
prow-commands: "/area
|
||||
/kind
|
||||
/milestone
|
||||
/retitle
|
||||
/remove
|
||||
/reopen
|
||||
/uncc
|
||||
/unassign
|
||||
/cc
|
||||
/uncc"
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
2
.github/workflows/push-builder.yml
vendored
2
.github/workflows/push-builder.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
# The default value is "1" which fetches only a single commit. If we merge PR without squash or rebase,
|
||||
# there are at least two commits: the first one is the merge commit and the second one is the real commit
|
||||
|
||||
31
.github/workflows/push.yml
vendored
31
.github/workflows/push.yml
vendored
@@ -16,39 +16,34 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.22.6'
|
||||
go-version: '1.20.10'
|
||||
id: go
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# Fix issue of setup-gcloud
|
||||
- run: |
|
||||
sudo apt-get install python2.7
|
||||
export CLOUDSDK_PYTHON="/usr/bin/python2"
|
||||
|
||||
- id: 'auth'
|
||||
uses: google-github-actions/auth@v2
|
||||
- uses: google-github-actions/setup-gcloud@v0
|
||||
with:
|
||||
credentials_json: '${{ secrets.GCS_SA_KEY }}'
|
||||
|
||||
- name: 'set up GCloud SDK'
|
||||
uses: google-github-actions/setup-gcloud@v2
|
||||
|
||||
- name: 'use gcloud CLI'
|
||||
run: |
|
||||
gcloud info
|
||||
version: '285.0.0'
|
||||
service_account_key: ${{ secrets.GCS_SA_KEY }}
|
||||
export_default_credentials: true
|
||||
- run: gcloud info
|
||||
|
||||
- name: Set up QEMU
|
||||
id: qemu
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
platforms: all
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
@@ -62,14 +57,14 @@ jobs:
|
||||
run: make test
|
||||
|
||||
- name: Upload test coverage
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: coverage.out
|
||||
verbose: true
|
||||
|
||||
# Use the JSON key in secret to login gcr.io
|
||||
- uses: 'docker/login-action@v3'
|
||||
- uses: 'docker/login-action@v2'
|
||||
with:
|
||||
registry: 'gcr.io' # or REGION.docker.pkg.dev
|
||||
username: '_json_key'
|
||||
@@ -81,7 +76,7 @@ jobs:
|
||||
run: |
|
||||
sudo swapoff -a
|
||||
sudo rm -f /mnt/swapfile
|
||||
docker system prune -a --force
|
||||
docker image prune -a --force
|
||||
|
||||
# Build and push Velero image to docker registry
|
||||
docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
4
.github/workflows/rebase.yml
vendored
4
.github/workflows/rebase.yml
vendored
@@ -9,10 +9,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Automatic Rebase
|
||||
uses: cirrus-actions/rebase@1.8
|
||||
uses: cirrus-actions/rebase@1.3.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
2
.github/workflows/stale-issues.yml
vendored
2
.github/workflows/stale-issues.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9.0.0
|
||||
- uses: actions/stale@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: "This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 14 days. If a Velero team member has requested log or more information, please provide the output of the shared commands."
|
||||
|
||||
@@ -16,7 +16,6 @@ If you're using Velero and want to add your organization to this list,
|
||||
<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>
|
||||
<a href="https://cloudcasa.io/" border="0" target="_blank"><img alt="cloudcasa.io" src="site/static/img/adopters/cloudcasa.svg" height="50"></a>
|
||||
<a href="https://azure.microsoft.com/" border="0" target="_blank"><img alt="azure.com" src="site/static/img/adopters/azure.svg" height="50"></a>
|
||||
## Success Stories
|
||||
|
||||
Below is a list of adopters of Velero in **production environments** that have
|
||||
@@ -63,10 +62,7 @@ Okteto integrates Velero in [Okteto Cloud][94] and [Okteto Enterprise][95] to pe
|
||||
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.<br>
|
||||
|
||||
**[CloudCasa][103]**<br>
|
||||
[Catalogic Software][104] integrates Velero with [CloudCasa][103] - A Smart Home in the Cloud for Backups. CloudCasa is a full-featured, scalable, cloud-native solution providing Kubernetes data protection, disaster recovery, and migration as a service. An option to manage existing Velero instances and an enterprise self-hosted option are also available.<br>
|
||||
|
||||
**[Microsoft Azure][105]**<br>
|
||||
[Azure Backup for AKS][106] is an Azure native, Kubernetes aware, Enterprise ready backup for containerized applications deployed on Azure Kubernetes Service (AKS). AKS Backup utilizes Velero to perform backup and restore operations to protect stateful applications in AKS clusters.<br>
|
||||
[Catalogic Software][104] integrates Velero with [CloudCasa][103] - A Smart Home in the Cloud for Backups. CloudCasa is a simple, scalable, cloud-native solution providing data protection and disaster recovery as a service. This solution is built using Kubernetes for protecting Kubernetes clusters.<br>
|
||||
|
||||
## Adding your organization to the list of Velero Adopters
|
||||
|
||||
@@ -122,6 +118,3 @@ If you would like to add your logo to a future `Adopters of Velero` section on [
|
||||
|
||||
[103]: https://cloudcasa.io/
|
||||
[104]: https://www.catalogicsoftware.com/
|
||||
|
||||
[105]: https://azure.microsoft.com/
|
||||
[106]: https://learn.microsoft.com/azure/backup/backup-overview
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
## Current release:
|
||||
* [CHANGELOG-1.14.md][24]
|
||||
* [CHANGELOG-1.11.md][21]
|
||||
|
||||
## Older releases:
|
||||
* [CHANGELOG-1.13.md][23]
|
||||
* [CHANGELOG-1.12.md][22]
|
||||
* [CHANGELOG-1.11.md][21]
|
||||
* [CHANGELOG-1.10.md][20]
|
||||
* [CHANGELOG-1.9.md][19]
|
||||
* [CHANGELOG-1.8.md][18]
|
||||
@@ -27,9 +24,6 @@
|
||||
* [CHANGELOG-0.3.md][1]
|
||||
|
||||
|
||||
[24]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.14.md
|
||||
[23]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.13.md
|
||||
[22]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.12.md
|
||||
[21]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.11.md
|
||||
[20]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.10.md
|
||||
[19]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.9.md
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
We as members, contributors, and leaders pledge to make participation in the Velero project and our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socioeconomic status,
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
# Velero binary build section
|
||||
FROM --platform=$BUILDPLATFORM golang:1.22.6-bookworm as velero-builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.20.10-bullseye as velero-builder
|
||||
|
||||
ARG GOPROXY
|
||||
ARG BIN
|
||||
@@ -47,7 +47,7 @@ RUN mkdir -p /output/usr/bin && \
|
||||
go clean -modcache -cache
|
||||
|
||||
# Restic binary build section
|
||||
FROM --platform=$BUILDPLATFORM golang:1.22.6-bookworm as restic-builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.20.10-bullseye as restic-builder
|
||||
|
||||
ARG BIN
|
||||
ARG TARGETOS
|
||||
@@ -70,7 +70,7 @@ RUN mkdir -p /output/usr/bin && \
|
||||
go clean -modcache -cache
|
||||
|
||||
# Velero image packing section
|
||||
FROM paketobuildpacks/run-jammy-tiny:0.2.46
|
||||
FROM paketobuildpacks/run-jammy-tiny:0.2.11
|
||||
|
||||
LABEL maintainer="Xun Jiang <jxun@vmware.com>"
|
||||
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
|
||||
## Maintainers
|
||||
|
||||
| Maintainer | GitHub ID | Affiliation |
|
||||
|---------------------|---------------------------------------------------------------|--------------------------------------------------|
|
||||
| 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/) |
|
||||
| Xun Jiang | [blackpiglet](https://github.com/blackpiglet) | [VMware](https://www.github.com/vmware/) |
|
||||
| Ming Qiu | [qiuming-best](https://github.com/qiuming-best) | [VMware](https://www.github.com/vmware/) |
|
||||
| Shubham Pampattiwar | [shubham-pampattiwar](https://github.com/shubham-pampattiwar) | [OpenShift](https://github.com/openshift) |
|
||||
| Yonghui Li | [Lyndon-Li](https://github.com/Lyndon-Li) | [VMware](https://www.github.com/vmware/) |
|
||||
| Anshul Ahuja | [anshulahuja98](https://github.com/anshulahuja98) | [Microsoft Azure](https://www.github.com/azure/) |
|
||||
| Maintainer | GitHub ID | Affiliation |
|
||||
|---------------------|---------------------------------------------------------------|-------------------------------------------|
|
||||
| Dave Smith-Uchida | [dsu-igeek](https://github.com/dsu-igeek) | [Kasten](https://github.com/kastenhq/) |
|
||||
| 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/) |
|
||||
| Xun Jiang | [blackpiglet](https://github.com/blackpiglet) | [VMware](https://www.github.com/vmware/) |
|
||||
| Ming Qiu | [qiuming-best](https://github.com/qiuming-best) | [VMware](https://www.github.com/vmware/) |
|
||||
| Shubham Pampattiwar | [shubham-pampattiwar](https://github.com/shubham-pampattiwar) | [OpenShift](https://github.com/openshift) |
|
||||
| Yonghui Li | [Lyndon-Li](https://github.com/Lyndon-Li) | [VMware](https://www.github.com/vmware/) |
|
||||
|
||||
## Emeritus Maintainers
|
||||
* Adnan Abdulhussein ([prydonius](https://github.com/prydonius))
|
||||
@@ -25,12 +25,12 @@
|
||||
* Carlisia Thompson ([carlisia](https://github.com/carlisia))
|
||||
* Bridget McErlean ([zubron](https://github.com/zubron))
|
||||
* JenTing Hsiao ([jenting](https://github.com/jenting))
|
||||
* Dave Smith-Uchida ([dsu-igeek](https://github.com/dsu-igeek))
|
||||
|
||||
|
||||
## Velero Contributors & Stakeholders
|
||||
|
||||
| Feature Area | Lead |
|
||||
|------------------------|:------------------------------------------------------------------------------------:|
|
||||
| Architect | Dave Smith-Uchida [dsu-igeek](https://github.com/dsu-igeek) |
|
||||
| Technical Lead | Daniel Jiang [reasonerjt](https://github.com/reasonerjt) |
|
||||
| Kubernetes CSI Liaison | |
|
||||
| Deployment | |
|
||||
|
||||
17
Makefile
17
Makefile
@@ -74,18 +74,8 @@ else
|
||||
GCR_IMAGE_TAGS ?= $(GCR_IMAGE):$(VERSION)
|
||||
endif
|
||||
|
||||
# check buildx is enabled
|
||||
# macOS/Windows docker cli without Docker Desktop license: https://github.com/abiosoft/colima
|
||||
# To add buildx to docker cli: https://github.com/abiosoft/colima/discussions/273#discussioncomment-2684502
|
||||
ifeq ($(shell docker buildx inspect 2>/dev/null | awk '/Status/ { print $$2 }'), running)
|
||||
BUILDX_ENABLED ?= true
|
||||
# if emulated docker cli from podman, assume enabled
|
||||
# emulated docker cli from podman: https://podman-desktop.io/docs/migrating-from-docker/emulating-docker-cli-with-podman
|
||||
# podman known issues:
|
||||
# - on remote podman, such as on macOS,
|
||||
# --output issue: https://github.com/containers/podman/issues/15922
|
||||
else ifeq ($(shell cat $(shell which docker) | grep -c "exec podman"), 1)
|
||||
BUILDX_ENABLED ?= true
|
||||
else
|
||||
BUILDX_ENABLED ?= false
|
||||
endif
|
||||
@@ -118,7 +108,6 @@ platform_temp = $(subst -, ,$(ARCH))
|
||||
GOOS = $(word 1, $(platform_temp))
|
||||
GOARCH = $(word 2, $(platform_temp))
|
||||
GOPROXY ?= https://proxy.golang.org
|
||||
GOBIN=$$(pwd)/.go/bin
|
||||
|
||||
# If you want to build all binaries, see the 'all-build' rule.
|
||||
# If you want to build all containers, see the 'all-containers' rule.
|
||||
@@ -140,7 +129,6 @@ local: build-dirs
|
||||
# Add DEBUG=1 to enable debug locally
|
||||
GOOS=$(GOOS) \
|
||||
GOARCH=$(GOARCH) \
|
||||
GOBIN=$(GOBIN) \
|
||||
VERSION=$(VERSION) \
|
||||
REGISTRY=$(REGISTRY) \
|
||||
PKG=$(PKG) \
|
||||
@@ -157,7 +145,6 @@ _output/bin/$(GOOS)/$(GOARCH)/$(BIN): build-dirs
|
||||
$(MAKE) shell CMD="-c '\
|
||||
GOOS=$(GOOS) \
|
||||
GOARCH=$(GOARCH) \
|
||||
GOBIN=$(GOBIN) \
|
||||
VERSION=$(VERSION) \
|
||||
REGISTRY=$(REGISTRY) \
|
||||
PKG=$(PKG) \
|
||||
@@ -370,11 +357,11 @@ gen-docs:
|
||||
|
||||
.PHONY: test-e2e
|
||||
test-e2e: local
|
||||
$(MAKE) -e VERSION=$(VERSION) -C test/ run-e2e
|
||||
$(MAKE) -e VERSION=$(VERSION) -C test/e2e run
|
||||
|
||||
.PHONY: test-perf
|
||||
test-perf: local
|
||||
$(MAKE) -e VERSION=$(VERSION) -C test/ run-perf
|
||||
$(MAKE) -e VERSION=$(VERSION) -C test/perf run
|
||||
|
||||
go-generate:
|
||||
go generate ./pkg/...
|
||||
24
OWNERS
24
OWNERS
@@ -1,24 +0,0 @@
|
||||
# This file is used by the [PROW action](https://github.com/jpmcb/prow-github-actions) to approve and merge PRs.
|
||||
# The file's format follows the [OWNERS SPEC](https://www.kubernetes.dev/docs/guide/owners/#owners-spec).
|
||||
|
||||
# List of usernames who may use /lgtm
|
||||
reviewers:
|
||||
- @Lyndon-Li
|
||||
- @anshulahuja98
|
||||
- @blackpiglet
|
||||
- @qiuming-best
|
||||
- @reasonerjt
|
||||
- @shubham-pampattiwar
|
||||
- @sseago
|
||||
- @ywk253100
|
||||
|
||||
# List of usernames who may use /approve
|
||||
approvers:
|
||||
- @Lyndon-Li
|
||||
- @anshulahuja98
|
||||
- @blackpiglet
|
||||
- @qiuming-best
|
||||
- @reasonerjt
|
||||
- @shubham-pampattiwar
|
||||
- @sseago
|
||||
- @ywk253100
|
||||
16
README.md
16
README.md
@@ -40,17 +40,17 @@ See [the list of releases][6] to find out about feature changes.
|
||||
|
||||
The following is a list of the supported Kubernetes versions for each Velero version.
|
||||
|
||||
| Velero version | Expected Kubernetes version compatibility | Tested on Kubernetes version |
|
||||
|----------------|-------------------------------------------|-------------------------------------|
|
||||
| 1.14 | 1.18-latest | 1.27.9, 1.28.9, and 1.29.4 |
|
||||
| 1.13 | 1.18-latest | 1.26.5, 1.27.3, 1.27.8, and 1.28.3 |
|
||||
| 1.12 | 1.18-latest | 1.25.7, 1.26.5, 1.26.7, and 1.27.3 |
|
||||
| 1.11 | 1.18-latest | 1.23.10, 1.24.9, 1.25.5, and 1.26.1 |
|
||||
| 1.10 | 1.18-latest | 1.22.5, 1.23.8, 1.24.6 and 1.25.1 |
|
||||
| Velero version | Expected Kubernetes version compatibility | Tested on Kubernetes version |
|
||||
|----------------|-------------------------------------------|----------------------------------------|
|
||||
| 1.12 | 1.18-latest | 1.25.7, 1.26.5, 1.27.6 and 1.28.0 |
|
||||
| 1.11 | 1.18-latest | 1.23.10, 1.24.9, 1.25.5, and 1.26.1 |
|
||||
| 1.10 | 1.18-latest | 1.22.5, 1.23.8, 1.24.6 and 1.25.1 |
|
||||
| 1.9 | 1.18-latest | 1.20.5, 1.21.2, 1.22.5, 1.23, and 1.24 |
|
||||
| 1.8 | 1.18-latest | |
|
||||
|
||||
Velero supports IPv4, IPv6, and dual stack environments. Support for this was tested against Velero v1.8.
|
||||
|
||||
The Velero maintainers are continuously working to expand testing coverage, but are not able to test every combination of Velero and supported Kubernetes versions for each Velero release. The table above is meant to track the current testing coverage and the expected supported Kubernetes versions for each Velero version.
|
||||
The Velero maintainers are continuously working to expand testing coverage, but are not able to test every combination of Velero and supported Kubernetes versions for each Velero release. The table above is meant to track the current testing coverage and the expected supported Kubernetes versions for each Velero version. If you have a question about test coverage before v1.9, please reach out in the [#velero-users](https://kubernetes.slack.com/archives/C6VCGP4MT) Slack channel.
|
||||
|
||||
If you are interested in using a different version of Kubernetes with a given Velero version, we'd recommend that you perform testing before installing or upgrading your environment. For full information around capabilities within a release, also see the Velero [release notes](https://github.com/vmware-tanzu/velero/releases) or Kubernetes [release notes](https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG). See the Velero [support page](https://velero.io/docs/latest/support-process/) for information about supported versions of Velero.
|
||||
|
||||
|
||||
2
Tiltfile
2
Tiltfile
@@ -52,7 +52,7 @@ git_sha = str(local("git rev-parse HEAD", quiet = True, echo_off = True)).strip(
|
||||
|
||||
tilt_helper_dockerfile_header = """
|
||||
# Tilt image
|
||||
FROM golang:1.22.6 as tilt-helper
|
||||
FROM golang:1.20.10 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 && \
|
||||
|
||||
@@ -1,3 +1,122 @@
|
||||
## v1.12.4
|
||||
### 2024-02-26
|
||||
|
||||
### Download
|
||||
https://github.com/vmware-tanzu/velero/releases/tag/v1.12.4
|
||||
|
||||
### Container Image
|
||||
`velero/velero:v1.12.4`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.12/
|
||||
|
||||
### Upgrading
|
||||
https://velero.io/docs/v1.12/upgrade-to-1.12/
|
||||
|
||||
### All changes
|
||||
* BackupRepositories associated with a BSL are invalidated when BSL is (re-)created. (#7397, @kaovilai)
|
||||
* Check resource Group Version and Kind is available in cluster before attempting restore to prevent being stuck. (#7337, @kaovilai)
|
||||
* Make "disable-informer-cache" option false(enabled) by default to keep it consistent with the help message (#7298, @ywk253100)
|
||||
* Add description markers for dataupload and datadownload CRDs (#7042, @shubham-pampattiwar)
|
||||
|
||||
## v1.12.3
|
||||
### 2024-01-09
|
||||
|
||||
### Download
|
||||
https://github.com/vmware-tanzu/velero/releases/tag/v1.12.3
|
||||
|
||||
### Container Image
|
||||
`velero/velero:v1.12.3`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.12/
|
||||
|
||||
### Upgrading
|
||||
https://velero.io/docs/v1.12/upgrade-to-1.12/
|
||||
|
||||
### All changes
|
||||
* Fix issue #7244. By the end of the upload, check the outstanding incomplete snapshots and delete them by calling ApplyRetentionPolicy (#7247, @Lyndon-Li)
|
||||
* Fix issue #7189, data mover generic restore - don't assume the first volume as the restore volume (#7203, @Lyndon-Li)
|
||||
* Update CSIVolumeSnapshotsCompleted in backup's status and the metric
|
||||
during backup finalize stage according to async operations content. (#7202, @blackpiglet)
|
||||
* Node agent restart enhancement (#7130, @qiuming-best)
|
||||
* Fix issue #6928, remove snapshot deletion timeout for PVB (#7283, @Lyndon-Li)
|
||||
|
||||
## v1.12.2
|
||||
### 2023-11-20
|
||||
|
||||
### Download
|
||||
https://github.com/vmware-tanzu/velero/releases/tag/v1.12.2
|
||||
|
||||
### Container Image
|
||||
`velero/velero:v1.12.2`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.12/
|
||||
|
||||
### Upgrading
|
||||
https://velero.io/docs/v1.12/upgrade-to-1.12/
|
||||
|
||||
### All changes
|
||||
* Fix issue #7068, due to a behavior of CSI external snapshotter, manipulations of VS and VSC may not be handled in the same order inside external snapshotter as the API is called. So add a protection finalizer to ensure the order (#7114, @Lyndon-Li)
|
||||
* Update Backup.Status.CSIVolumeSnapshotsCompleted during finalize (#7111, @kaovilai)
|
||||
* Cherry-pick #6917 - Support JSON Merge Patch and Strategic Merge Patch in Resource Modifiers (#7049, @27149chen)
|
||||
* Bump up Velero base image to latest patch release (#7110, @allenxu404)
|
||||
* Fix the node-agent missing metrics-address defines. (#7098, @yanggangtony)
|
||||
* Fix issue #7094, fallback to full backup if previous snapshot is not found (#7097, @Lyndon-Li)
|
||||
* Add DataUpload Result and CSI VolumeSnapshot check for restore PV. (#7087, @blackpiglet)
|
||||
* Fix issue #7027, data mover backup exposer should not assume the first volume as the backup volume in backup pod (#7060, @Lyndon-Li)
|
||||
* Truncate the credential file to avoid the change of secret content messing it up (#7058, @ywk253100)
|
||||
* restore: Use warning when Create IsAlreadyExist and Get error (#7054, @kaovilai)
|
||||
* Read information from the credential specified by BSL (#7033, @ywk253100)
|
||||
* Fix issue 6913: Velero Built-in Datamover: Backup stucks in phase WaitingForPluginOperations when Node Agent pod gets restarted (#7025, @shubham-pampattiwar)
|
||||
* Fix unified repository (kopia) s3 credentials profile selection (#6997, @kaovilai)
|
||||
|
||||
## v1.12.1
|
||||
### 2023-10-20
|
||||
|
||||
### Download
|
||||
https://github.com/vmware-tanzu/velero/releases/tag/v1.12.1
|
||||
|
||||
### Container Image
|
||||
`velero/velero:v1.12.1`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.12/
|
||||
|
||||
### Upgrading
|
||||
https://velero.io/docs/v1.12/upgrade-to-1.12/
|
||||
|
||||
### Highlights
|
||||
|
||||
#### Data Mover Adds Support for Block Mode Volumes
|
||||
For PersistentVolumes with volumeMode set as Block, the volumes are mounted as raw block devices in pods, in 1.12.1, Velero CSI snapshot data movement supports to backup and restore this kind of volumes under linux based Kubernetes clusters.
|
||||
|
||||
#### New Parameter in Installation to Enable Data Mover
|
||||
The `velero install` sub-command now includes a new parameter,`--default-snapshot-move-data`, which configures Velero server to move data by default for all snapshots supporting data movement. This feature is useful for users who will always want to use VBDM for backups instead of plain CSI , as they no longer need to specify the `--snapshot-move-data` flag for each individual backup.
|
||||
|
||||
#### Velero Base Image change
|
||||
The base image previously used by Velero was `distroless`, which contains several CVEs cannot be addressed quickly. As a result, Velero will now use `paketobuildpacks` image starting from this new version.
|
||||
|
||||
### Limitations/Known issues
|
||||
* The data mover's support for block mode volumes is currently only applicable to Linux environments.
|
||||
|
||||
### All changes
|
||||
* Import auth provider plugins (#6970, @0x113)
|
||||
* Perf improvements for existing resource restore (#6948, @sseago)
|
||||
* Retry failed create when using generateName (#6943, @sseago)
|
||||
* Fix issue #6647, add the --default-snapshot-move-data parameter to Velero install, so that users don't need to specify --snapshot-move-data per backup when they want to move snapshot data for all backups (#6940, @Lyndon-Li)
|
||||
* Partially fix #6734, guide Kubernetes' scheduler to spread backup pods evenly across nodes as much as possible, so that data mover backup could achieve better parallelism (#6935, @Lyndon-Li)
|
||||
* Replace the base image with paketobuildpacks image (#6934, @ywk253100)
|
||||
* Add support for block volumes with Kopia (#6897, @dzaninovic)
|
||||
* Set ParallelUploadAboveSize as MaxInt64 and flush repo after setting up policy so that policy is retrieved correctly by TreeForSource (#6886, @Lyndon-Li)
|
||||
* Kubernetes 1.27 new job label batch.kubernetes.io/controller-uid are deleted during restore per https://github.com/kubernetes/kubernetes/pull/114930 (#6713, @kaovilai)
|
||||
* Add `orLabelSelectors` for backup, restore commands (#6881, @nilesh-akhade)
|
||||
* Fix issue #6859, move plugin depending podvolume functions to util pkg, so as to remove the dependencies to unnecessary repository packages like kopia, azure, etc. (#6877, @Lyndon-Li)
|
||||
* Fix issue #6786, always delete VSC regardless of the deletion policy (#6873, @Lyndon-Li)
|
||||
* Fix #6988, always get region from BSL if it is not empty (#6991, @Lyndon-Li)
|
||||
* Add both non-Windows version and Windows version code for PVC block mode logic. (#6986, @blackpiglet)
|
||||
|
||||
## v1.12
|
||||
### 2023-08-18
|
||||
|
||||
@@ -23,17 +142,17 @@ CSI Snapshot Data Movement is useful in below scenarios:
|
||||
* For on-premises users, the storage usually doesn't support durable snapshots, so it is impossible/less efficient/cost ineffective to keep volume snapshots by the storage This feature helps to move the snapshot data to a storage with lower cost and larger scale for long time preservation.
|
||||
* For public cloud users, this feature helps users to fulfill the multiple cloud strategy. It allows users to back up volume snapshots from one cloud provider and preserve or restore the data to another cloud provider. Then users will be free to flow their business data across cloud providers based on Velero backup and restore
|
||||
|
||||
CSI Snapshot Data Movement is built according to the Volume Snapshot Data Movement design ([Volume Snapshot Data Movement](https://github.com/vmware-tanzu/velero/blob/main/design/Implemented/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md)). More details can be found in the design.
|
||||
CSI Snapshot Data Movement is built according to the Volume Snapshot Data Movement design ([Volume Snapshot Data Movement design](https://github.com/vmware-tanzu/velero/blob/main/design/volume-snapshot-data-movement/volume-snapshot-data-movement.md)). Additionally, guidance on how to use the feature can be found in the Volume Snapshot Data Movement doc([Volume Snapshot Data Movement doc](https://velero.io/docs/v1.12/csi-snapshot-data-movement)).
|
||||
|
||||
#### Resource Modifiers
|
||||
In many use cases, customers often need to substitute specific values in Kubernetes resources during the restoration process like changing the namespace, changing the storage class, etc.
|
||||
|
||||
To address this need, Resource Modifiers (also known as JSON Substitutions) offer a generic solution in the restore workflow. It allows the user to define filters for specific resources and then specify a JSON patch (operator, path, value) to apply to the resource. This feature simplifies the process of making substitutions without requiring the implementation of a new RestoreItemAction plugin. More details can be found in Volume Snapshot Resource Modifiers design ([Resource Modifiers](https://github.com/vmware-tanzu/velero/blob/main/design/Implemented/json-substitution-action-design.md)).
|
||||
To address this need, Resource Modifiers (also known as JSON Substitutions) offer a generic solution in the restore workflow. It allows the user to define filters for specific resources and then specify a JSON patch (operator, path, value) to apply to the resource. This feature simplifies the process of making substitutions without requiring the implementation of a new RestoreItemAction plugin. More design details can be found in Resource Modifiers design ([Resource Modifiers design](https://github.com/vmware-tanzu/velero/blob/main/design/Implemented/json-substitution-action-design.md)). For instructions on how to use the feature, please refer to Resource Modifiers doc([Resource Modifiers doc](https://velero.io/docs/v1.12/restore-resource-modifiers)).
|
||||
|
||||
#### Multiple VolumeSnapshotClasses
|
||||
Prior to version 1.12, the Velero CSI plugin would choose the VolumeSnapshotClass in the cluster based on matching driver names and the presence of the "velero.io/csi-volumesnapshot-class" label. However, this approach proved inadequate for many user scenarios.
|
||||
|
||||
With the introduction of version 1.12, Velero now offers support for multiple VolumeSnapshotClasses in the CSI Plugin, enabling users to select a specific class for a particular backup. More details can be found in Multiple VolumeSnapshotClasses design ([Multiple VolumeSnapshotClasses](https://github.com/vmware-tanzu/velero/blob/main/design/Implemented/multiple-csi-volumesnapshotclass-support.md)).
|
||||
With the introduction of version 1.12, Velero now offers support for multiple VolumeSnapshotClasses in the CSI Plugin, enabling users to select a specific class for a particular backup. More design details can be found in Multiple VolumeSnapshotClasses design ([Multiple VolumeSnapshotClasses design](https://github.com/vmware-tanzu/velero/blob/main/design/Implemented/multiple-csi-volumesnapshotclass-support.md)). For instructions on how to use the feature, please refer to Multiple VolumeSnapshotClasses doc ([Multiple VolumeSnapshotClasses doc](https://velero.io/docs/v1.12/csi/#implementation-choices)).
|
||||
|
||||
#### Restore Finalizer
|
||||
Before v1.12, the restore controller would only delete restore resources but wouldn’t delete restore data from the backup storage location when the command `velero restore delete` was executed. The only chance Velero deletes restores data from the backup storage location is when the associated backup is deleted.
|
||||
@@ -51,10 +170,12 @@ To fix CVEs and keep pace with Golang, Velero made changes as follows:
|
||||
* Prior to v1.12, the parameter `uploader-type` for Velero installation had a default value of "restic". However, starting from this version, the default value has been changed to "kopia". This means that Velero will now use Kopia as the default path for file system backup.
|
||||
* The ways of setting CSI snapshot time have changed in v1.12. First, the sync waiting time for creating a snapshot handle in the CSI plugin is changed from the fixed 10 minutes into backup.Spec.CSISnapshotTimeout. The second, the async waiting time for VolumeSnapshot and VolumeSnapshotContent's status turning into `ReadyToUse` in operation uses the operation's timeout. The default value is 4 hours.
|
||||
* As from [Velero helm chart v4.0.0](https://github.com/vmware-tanzu/helm-charts/releases/tag/velero-4.0.0), it supports multiple BSL and VSL, and the BSL and VSL have changed from the map into a slice, and[ this breaking change](https://github.com/vmware-tanzu/helm-charts/pull/413) is not backward compatible. So it would be best to change the BSL and VSL configuration into slices before the Upgrade.
|
||||
* Prior to v1.12, deleting the Velero namespace would easily remove all the resources within it. However, with the introduction of finalizers attached to the Velero CR including `restore`, `dataupload`, and `datadownload` in this version, directly deleting Velero namespace may get stuck indefinitely because the pods responsible for handling the finalizers might be deleted before the resources attached to the finalizers. To avoid this issue, please use the command `velero uninstall` to delete all the Velero resources or ensure that you handle the finalizer appropriately before deleting the Velero namespace.
|
||||
|
||||
|
||||
### Limitations/Known issues
|
||||
* The Azure plugin supports Azure AD Workload identity way, but it only works for Velero native snapshots. It cannot support filesystem backup and snapshot data mover scenarios.
|
||||
* File System backup under Kopia path and CSI Snapshot Data Movement backup fail to back up files that are large the 2GiB due to issue https://github.com/vmware-tanzu/velero/issues/6668.
|
||||
|
||||
|
||||
### All Changes
|
||||
@@ -132,3 +253,10 @@ prior PVC restores with CSI (#6111, @eemcmullan)
|
||||
* Make GetPluginConfig accessible from other packages. (#6151, @tkaovila)
|
||||
* Ignore not found error during patching managedFields (#6136, @ywk253100)
|
||||
* Fix the goreleaser issues and add a new goreleaser action (#6109, @blackpiglet)
|
||||
* Add CSI snapshot data movement doc (#6793, @Lyndon-Li)
|
||||
* Use old(origin) namespace in resource modifier conditions in case namespace may change during restore (#6724, @27149chen)
|
||||
* Fix #6752: add namespace exclude check. (#6762, @blackpiglet)
|
||||
* Update restore controller logic for restore deletion (#6761, @ywk253100)
|
||||
* Fix issue #6753, remove the check for read-only BSL in restore async operation controller since Velero cannot fully support read-only mode BSL in restore at present (#6758, @Lyndon-Li)
|
||||
* Fixes #6636, skip subresource in resource discovery (#6688, @27149chen)
|
||||
* This pr made some improvements in Resource Modifiers:1. add label selector 2. change the field name from groupKind to groupResource (#6704, @27149chen)
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
## v1.13
|
||||
### 2024-01-10
|
||||
|
||||
### Download
|
||||
https://github.com/vmware-tanzu/velero/releases/tag/v1.13.0
|
||||
|
||||
### Container Image
|
||||
`velero/velero:v1.13.0`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.13/
|
||||
|
||||
### Upgrading
|
||||
https://velero.io/docs/v1.13/upgrade-to-1.13/
|
||||
|
||||
### Highlights
|
||||
|
||||
#### Resource Modifier Enhancement
|
||||
Velero introduced the Resource Modifiers in v1.12.0. This feature allows users to specify a ConfigMap with a set of rules to modify the resources during restoration. However, only the JSON Patch is supported when creating the rules, and JSON Patch has some limitations, which cannot cover all use cases. In v1.13.0, Velero adds new support for JSON Merge Patch and Strategic Merge Patch, which provide more power and flexibility and allow users to use the same ConfigMap to apply patches on the resources. More design details can be found in [Support JSON Merge Patch and Strategic Merge Patch in Resource Modifiers](https://github.com/vmware-tanzu/velero/blob/main/design/Implemented/merge-patch-and-strategic-in-resource-modifier.md) design. For instructions on how to use the feature, please refer to the [Resource Modifiers](https://velero.io/docs/v1.13/restore-resource-modifiers/) doc.
|
||||
|
||||
#### Node-Agent Concurrency
|
||||
Velero data movement activities from fs-backups and CSI snapshot data movements run in Velero node-agent, so may be hosted by every node in the cluster and consume resources (i.e. CPU, memory, network bandwidth) from there. With v1.13, users are allowed to configure how many data movement activities (a.k.a, loads) run in each node globally or by node, so that users can better leverage the performance of Velero data movement activities and the resource consumption in the cluster. For more information, check the [Node-Agent Concurrency](https://velero.io/docs/v1.13/node-agent-concurrency/) document.
|
||||
|
||||
#### Parallel Files Upload Options
|
||||
Velero now supports configurable options for parallel files upload when using Kopia uploader to do fs-backups or CSI snapshot data movements which makes speed up backup possible.
|
||||
For more information, please check [Here](https://velero.io/docs/v1.13/backup-reference/#parallel-files-upload).
|
||||
|
||||
#### Write Sparse Files Options
|
||||
If using fs-restore or CSI snapshot data movements, it’s supported to write sparse files during restore. For more information, please check [Here](https://velero.io/docs/v1.13/restore-reference/#write-sparse-files).
|
||||
|
||||
#### Backup Describe
|
||||
In v1.13, the Backup Volume section is added to the velero backup describe command output. The backup Volumes section describes information for all the volumes included in the backup of various backup types, i.e. native snapshot, fs-backup, CSI snapshot, and CSI snapshot data movement. Particularly, the velero backup description now supports showing the information of CSI snapshot data movements, which is not supported in v1.12.
|
||||
|
||||
Additionally, backup describe command will not check EnableCSI feature gate from client side, so if a backup has volumes with CSI snapshot or CSI snapshot data movement, backup describe command always shows the corresponding information in its output.
|
||||
|
||||
#### Backup's new VolumeInfo metadata
|
||||
Create a new metadata file in the backup repository's backup name sub-directory to store the backup-including PVC and PV information. The information includes the backing-up method of the PVC and PV data, snapshot information, and status. The VolumeInfo metadata file determines how the PV resource should be restored. The Velero downstream software can also use this metadata file to get a summary of the backup's volume data information.
|
||||
|
||||
#### Enhancement for CSI Snapshot Data Movements when Velero Pod Restart
|
||||
When performing backup and restore operations, enhancements have been implemented for Velero server pods or node agents to ensure that the current backup or restore process is not stuck or interrupted after restart due to certain exceptional circumstances.
|
||||
|
||||
#### New status fields added to show hook execution details
|
||||
Hook execution status is now included in the backup/restore CR status and displayed in the backup/restore describe command output. Specifically, it will show the number of hooks which attempted to execute under the HooksAttempted field and the number of hooks which failed to execute under the HooksFailed field.
|
||||
|
||||
#### AWS SDK Bump Up
|
||||
Bump up AWS SDK for Go to version 2, which offers significant performance improvements in CPU and memory utilization over version 1.
|
||||
|
||||
#### Azure AD/Workload Identity Support
|
||||
Azure AD/Workload Identity is the recommended approach to do the authentication with Azure services/AKS, Velero has introduced support for Azure AD/Workload Identity on the Velero Azure plugin side in previous releases, and in v1.13.0 Velero adds new support for Kopia operations(file system backup/data mover/etc.) with Azure AD/Workload Identity.
|
||||
|
||||
#### Runtime and dependencies
|
||||
To fix CVEs and keep pace with Golang, Velero made changes as follows:
|
||||
* Bump Golang runtime to v1.21.6.
|
||||
* Bump several dependent libraries to new versions.
|
||||
* Bump Kopia to v0.15.0.
|
||||
|
||||
|
||||
### Breaking changes
|
||||
* Backup describe command: due to the backup describe output enhancement, some existing information (i.e. the output for native snapshot, CSI snapshot, and fs-backup) has been moved to the Backup Volumes section with some format changes.
|
||||
* API type changes: changes the field [DataMoverConfig](https://github.com/vmware-tanzu/velero/blob/v1.13.0/pkg/apis/velero/v2alpha1/data_upload_types.go#L54) in DataUploadSpec from `*map[string][string]`` to `map[string]string`
|
||||
* Velero install command: due to the issue [#7264](https://github.com/vmware-tanzu/velero/issues/7264), v1.13.0 introduces a break change that make the informer cache enabled by default to keep the actual behavior consistent with the helper message(the informer cache is disabled by default before the change).
|
||||
|
||||
|
||||
### Limitations/Known issues
|
||||
* The backup's VolumeInfo metadata doesn't have the information updated in the async operations. This function could be supported in v1.14 release.
|
||||
|
||||
### Note
|
||||
* Velero introduces the informer cache which is enabled by default. The informer cache improves the restore performance but may cause higher memory consumption. Increase the memory limit of the Velero pod or disable the informer cache by specifying the `--disable-informer-cache` option when installing Velero if you get the OOM error.
|
||||
|
||||
### Deprecation announcement
|
||||
* The generated k8s clients, informers, and listers are deprecated in the Velero v1.13 release. They are put in the Velero repository's pkg/generated directory. According to the n+2 supporting policy, the deprecated are kept for two more releases. The pkg/generated directory should be deleted in the v1.15 release.
|
||||
* After the backup VolumeInfo metadata file is added to the backup, Velero decides how to restore the PV resource according to the VolumeInfo content. To support the backup generated by the older version of Velero, the old logic is also kept. The support for the backup without the VolumeInfo metadata file will be kept for two releases. The support logic will be deleted in the v1.15 release.
|
||||
|
||||
### All Changes
|
||||
* Make "disable-informer-cache" option false(enabled) by default to keep it consistent with the help message (#7294, @ywk253100)
|
||||
* Fix issue #6928, remove snapshot deletion timeout for PVB (#7282, @Lyndon-Li)
|
||||
* Do not set "targetNamespace" to namespace items (#7274, @reasonerjt)
|
||||
* Fix issue #7244. By the end of the upload, check the outstanding incomplete snapshots and delete them by calling ApplyRetentionPolicy (#7245, @Lyndon-Li)
|
||||
* Adjust the newline output of resource list in restore describer (#7238, @allenxu404)
|
||||
* Remove the redundant newline in backup describe output (#7229, @allenxu404)
|
||||
* Fix issue #7189, data mover generic restore - don't assume the first volume as the restore volume (#7201, @Lyndon-Li)
|
||||
* Update CSIVolumeSnapshotsCompleted in backup's status and the metric
|
||||
during backup finalize stage according to async operations content. (#7184, @blackpiglet)
|
||||
* Refactor DownloadRequest Stream function (#7175, @blackpiglet)
|
||||
* Add `--skip-immediately` flag to schedule commands; `--schedule-skip-immediately` server and install (#7169, @kaovilai)
|
||||
* Add node-agent concurrency doc and change the config name from dataPathConcurrency to loadCocurrency (#7161, @Lyndon-Li)
|
||||
* Enhance hooks tracker by adding a returned error to record function (#7153, @allenxu404)
|
||||
* Track the skipped PV when SnapshotVolumes set as false (#7152, @reasonerjt)
|
||||
* Add more linters part 2. (#7151, @blackpiglet)
|
||||
* Fix issue #7135, check pod status before checking node-agent pod status (#7150, @Lyndon-Li)
|
||||
* Treat namespace as a regular restorable item (#7143, @reasonerjt)
|
||||
* Allow sparse option for Kopia & Restic restore (#7141, @qiuming-best)
|
||||
* Use VolumeInfo to help restore the PV. (#7138, @blackpiglet)
|
||||
* Node agent restart enhancement (#7130, @qiuming-best)
|
||||
* Fix issue #6695, add describe for data mover backups (#7125, @Lyndon-Li)
|
||||
* Add hooks status to backup/restore CR (#7117, @allenxu404)
|
||||
* Include plugin name in the error message by operations (#7115, @reasonerjt)
|
||||
* Fix issue #7068, due to a behavior of CSI external snapshotter, manipulations of VS and VSC may not be handled in the same order inside external snapshotter as the API is called. So add a protection finalizer to ensure the order (#7102, @Lyndon-Li)
|
||||
* Generate VolumeInfo for backup. (#7100, @blackpiglet)
|
||||
* Fix issue #7094, fallback to full backup if previous snapshot is not found (#7096, @Lyndon-Li)
|
||||
* Fix issue #7068, due to an behavior of CSI external snapshotter, manipulations of VS and VSC may not be handled in the same order inside external snapshotter as the API is called. So add a protection finalizer to ensure the order (#7095, @Lyndon-Li)
|
||||
* Skip syncing the backup which doesn't contain backup metadata (#7081, @ywk253100)
|
||||
* Fix issue #6693, partially fail restore if CSI snapshot is involved but CSI feature is not ready, i.e., CSI feature gate is not enabled or CSI plugin is not installed. (#7077, @Lyndon-Li)
|
||||
* Truncate the credential file to avoid the change of secret content messing it up (#7072, @ywk253100)
|
||||
* Add VolumeInfo metadata structures. (#7070, @blackpiglet)
|
||||
* improve discoveryHelper.Refresh() in restore (#7069, @27149chen)
|
||||
* Add DataUpload Result and CSI VolumeSnapshot check for restore PV. (#7061, @blackpiglet)
|
||||
* Add the implementation for design #6950, configurable data path concurrency (#7059, @Lyndon-Li)
|
||||
* Make data mover fail early (#7052, @qiuming-best)
|
||||
* Remove dependency of generated client part 3. (#7051, @blackpiglet)
|
||||
* Update Backup.Status.CSIVolumeSnapshotsCompleted during finalize (#7046, @kaovilai)
|
||||
* Remove the Velero generated client. (#7041, @blackpiglet)
|
||||
* Fix issue #7027, data mover backup exposer should not assume the first volume as the backup volume in backup pod (#7038, @Lyndon-Li)
|
||||
* Read information from the credential specified by BSL (#7034, @ywk253100)
|
||||
* Fix #6857. Added check for matching Owner References when synchronizing backups, removing references that are not found/have mismatched uid. (#7032, @deefdragon)
|
||||
* Add description markers for dataupload and datadownload CRDs (#7028, @shubham-pampattiwar)
|
||||
* Add HealthCheckNodePort deletion logic for Service restore. (#7026, @blackpiglet)
|
||||
* Fix inconsistent behavior of Backup and Restore hook execution (#7022, @allenxu404)
|
||||
* Fix #6964. Don't use csiSnapshotTimeout (10 min) for waiting snapshot to readyToUse for data mover, so as to make the behavior complied with CSI snapshot backup (#7011, @Lyndon-Li)
|
||||
* restore: Use warning when Create IsAlreadyExist and Get error (#7004, @kaovilai)
|
||||
* Bump kopia to 0.15.0 (#7001, @Lyndon-Li)
|
||||
* Make Kopia file parallelism configurable (#7000, @qiuming-best)
|
||||
* Fix unified repository (kopia) s3 credentials profile selection (#6995, @kaovilai)
|
||||
* Fix #6988, always get region from BSL if it is not empty (#6990, @Lyndon-Li)
|
||||
* Limit PVC block mode logic to non-Windows platform. (#6989, @blackpiglet)
|
||||
* It is a valid case that the Status.RestoreSize field in VolumeSnapshot is not set, if so, get the volume size from the source PVC to create the backup PVC (#6976, @Lyndon-Li)
|
||||
* Check whether the action is a CSI action and whether CSI feature is enabled, before executing the action. (#6968, @blackpiglet)
|
||||
* Add the PV backup information design document. (#6962, @blackpiglet)
|
||||
* Change controller-runtime List option from MatchingFields to ListOptions (#6958, @blackpiglet)
|
||||
* Add the design for node-agent concurrency (#6950, @Lyndon-Li)
|
||||
* Import auth provider plugins (#6947, @0x113)
|
||||
* Fix #6668, add a limitation for file system restore parallelism with other types of restores (CSI snapshot restore, CSI snapshot movement restore) (#6946, @Lyndon-Li)
|
||||
* Add MSI Support for Azure plugin. (#6938, @yanggangtony)
|
||||
* Partially fix #6734, guide Kubernetes' scheduler to spread backup pods evenly across nodes as much as possible, so that data mover backup could achieve better parallelism (#6926, @Lyndon-Li)
|
||||
* Bump up aws sdk to aws-sdk-go-v2 (#6923, @reasonerjt)
|
||||
* Optional check if targeted container is ready before executing a hook (#6918, @Ripolin)
|
||||
* Support JSON Merge Patch and Strategic Merge Patch in Resource Modifiers (#6917, @27149chen)
|
||||
* Fix issue 6913: Velero Built-in Datamover: Backup stucks in phase WaitingForPluginOperations when Node Agent pod gets restarted (#6914, @shubham-pampattiwar)
|
||||
* Set ParallelUploadAboveSize as MaxInt64 and flush repo after setting up policy so that policy is retrieved correctly by TreeForSource (#6885, @Lyndon-Li)
|
||||
* Replace the base image with paketobuildpacks image (#6883, @ywk253100)
|
||||
* Fix issue #6859, move plugin depending podvolume functions to util pkg, so as to remove the dependencies to unnecessary repository packages like kopia, azure, etc. (#6875, @Lyndon-Li)
|
||||
* Fix #6861. Only Restic path requires repoIdentifier, so for non-restic path, set the repoIdentifier fields as empty in PVB and PVR and also remove the RepoIdentifier column in the get output of PVBs and PVRs (#6872, @Lyndon-Li)
|
||||
* Add volume types filter in resource policies (#6863, @qiuming-best)
|
||||
* change the metrics backup_attempt_total default value to 1. (#6838, @yanggangtony)
|
||||
* Bump kopia to v0.14 (#6833, @Lyndon-Li)
|
||||
* Retry failed create when using generateName (#6830, @sseago)
|
||||
* Fix issue #6786, always delete VSC regardless of the deletion policy (#6827, @Lyndon-Li)
|
||||
* Proposal to support JSON Merge Patch and Strategic Merge Patch in Resource Modifiers (#6797, @27149chen)
|
||||
* Fix the node-agent missing metrics-address defines. (#6784, @yanggangtony)
|
||||
* Fix default BSL setting not work (#6771, @qiuming-best)
|
||||
* Update restore controller logic for restore deletion (#6770, @ywk253100)
|
||||
* Fix #6752: add namespace exclude check. (#6760, @blackpiglet)
|
||||
* Fix issue #6753, remove the check for read-only BSL in restore async operation controller since Velero cannot fully support read-only mode BSL in restore at present (#6757, @Lyndon-Li)
|
||||
* Fix issue #6647, add the --default-snapshot-move-data parameter to Velero install, so that users don't need to specify --snapshot-move-data per backup when they want to move snapshot data for all backups (#6751, @Lyndon-Li)
|
||||
* Use old(origin) namespace in resource modifier conditions in case namespace may change during restore (#6724, @27149chen)
|
||||
* Perf improvements for existing resource restore (#6723, @sseago)
|
||||
* Remove schedule-related metrics on schedule delete (#6715, @nilesh-akhade)
|
||||
* Kubernetes 1.27 new job label batch.kubernetes.io/controller-uid are deleted during restore per https://github.com/kubernetes/kubernetes/pull/114930 (#6712, @kaovilai)
|
||||
* This pr made some improvements in Resource Modifiers: 1. add label selector 2. change the field name from groupKind to groupResource (#6704, @27149chen)
|
||||
* Make Kopia support Azure AD (#6686, @ywk253100)
|
||||
* Add support for block volumes with Kopia (#6680, @dzaninovic)
|
||||
* Delete PartiallyFailed orphaned backups as well as Completed ones (#6649, @sseago)
|
||||
* Add CSI snapshot data movement doc (#6637, @Lyndon-Li)
|
||||
* Fixes #6636, skip subresource in resource discovery (#6635, @27149chen)
|
||||
* Add `orLabelSelectors` for backup, restore commands (#6475, @nilesh-akhade)
|
||||
* fix run preHook and postHook on completed pods (#5211, @cleverhu)
|
||||
@@ -1,131 +0,0 @@
|
||||
## v1.14.1
|
||||
|
||||
### Download
|
||||
https://github.com/vmware-tanzu/velero/releases/tag/v1.14.1
|
||||
|
||||
### Container Image
|
||||
`velero/velero:v1.14.1`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.14/
|
||||
|
||||
### Upgrading
|
||||
https://velero.io/docs/v1.14/upgrade-to-1.14/
|
||||
|
||||
### All Changes
|
||||
* Avoid wrapping failed PVB status with empty message. (#8037, @mrnold)
|
||||
* Make PVPatchMaximumDuration timeout configurable (#8035, @shubham-pampattiwar)
|
||||
* Reuse existing plugin manager for get/put volume info (#8016, @sseago)
|
||||
* Skip PV patch step in Restoe workflow for WaitForFirstConsumer VolumeBindingMode Pending state PVCs (#8006, @shubham-pampattiwar)
|
||||
* Check whether the namespaces specified in namespace filter exist. (#7998, @blackpiglet)
|
||||
* Check whether the volume's source is PVC before fetching its PV. (#7976, @blackpiglet)
|
||||
* Fix issue #7904, add the limitation clarification for change PVC selected-node feature (#7949, @Lyndon-Li)
|
||||
* Expose the VolumeHelper to third-party plugins. (#7944, @blackpiglet)
|
||||
* Don't consider unschedulable pods unrecoverable (#7926, @sseago)
|
||||
|
||||
|
||||
## v1.14
|
||||
|
||||
### Download
|
||||
https://github.com/vmware-tanzu/velero/releases/tag/v1.14.0
|
||||
|
||||
### Container Image
|
||||
`velero/velero:v1.14.0`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.14/
|
||||
|
||||
### Upgrading
|
||||
https://velero.io/docs/v1.14/upgrade-to-1.14/
|
||||
|
||||
### Highlights
|
||||
|
||||
#### The maintenance work for kopia/restic backup repositories is run in jobs
|
||||
Since velero started using kopia as the approach for filesystem-level backup/restore, we've noticed an issue when velero connects to the kopia backup repositories and performs maintenance, it sometimes consumes excessive memory that can cause the velero pod to get OOM Killed. To mitigate this issue, the maintenance work will be moved out of velero pod to a separate kubernetes job, and the user will be able to specify the resource request in "velero install".
|
||||
#### Volume Policies are extended to support more actions to handle volumes
|
||||
In an earlier release, a flexible volume policy was introduced to skip certain volumes from a backup. In v1.14 we've made enhancement to this policy to allow the user to set how the volumes should be backed up. The user will be able to set "fs-backup" or "snapshot" as value of “action" in the policy and velero will backup the volumes accordingly. This enhancement allows the user to achieve a fine-grained control like "opt-in/out" without having to update the target workload. For more details please refer to https://velero.io/docs/v1.14/resource-filtering/#supported-volumepolicy-actions
|
||||
#### Node Selection for Data Movement Backup
|
||||
In velero the data movement flow relies on datamover pods, and these pods may take substantial resources and keep running for a long time. In v1.14, the user will be able to create a configmap to define the eligible nodes on which the datamover pods are launched. For more details refer to https://velero.io/docs/v1.14/data-movement-backup-node-selection/
|
||||
#### VolumeInfo metadata for restored volumes
|
||||
In v1.13, we introduced volumeinfo metadata for backup to help velero CLI and downstream adopter understand how velero handles each volume during backup. In v1.14, similar metadata will be persisted for each restore. velero CLI is also updated to bring more info in the output of "velero restore describe".
|
||||
#### "Finalizing" phase is introduced to restores
|
||||
The "Finalizing" phase is added to the state transition flow to restore, which helps us fix several issues: The labels added to PVs will be restored after the data in the PV is restored via volumesnapshotter. The post restore hook will be executed after datamovement is finished.
|
||||
#### Certificate-based authentication support for Azure
|
||||
Besides the service principal with secret(password)-based authentication, Velero introduces the new support for service principal with certificate-based authentication in v1.14.0. This approach enables you to adopt a phishing resistant authentication by using conditional access policies, which better protects Azure resources and is the recommended way by Azure.
|
||||
|
||||
### Runtime and dependencies
|
||||
* Golang runtime: v1.22.2
|
||||
* kopia: v0.17.0
|
||||
|
||||
### Limitations/Known issues
|
||||
* For the external BackupItemAction plugins that take snapshots for PVs, such as vsphere plugin. If the plugin checks the value of the field "snapshotVolumes" in the backup spec as a criteria for snapshot, the settings in the volume policy will not take effect. For example, if the "snapshotVolumes" is set to False in the backup spec, but a volume meets the condition in the volume policy for "snapshot" action, because the plugin will not check the settings in the volume policy, the plugin will not take snapshot for the volume. For more details please refer to #7818
|
||||
|
||||
### Breaking changes
|
||||
* CSI plugin has been merged into velero repo in v1.14 release. It will be installed by default as an internal plugin, and should not be installed via "–plugins " parameter in "velero install" command.
|
||||
* The default resource requests and limitations for node agent are removed in v1.14, to make the node agent pods have the QoS class of "BestEffort", more details please refer to #7391
|
||||
* There's a change in namespace filtering behavior during backup: In v1.14, when the includedNamespaces/excludedNamespaces fields are not set and the labelSelector/OrLabelSelectors are set in the backup spec, the backup will only include the namespaces which contain the resources that match the label selectors, while in previous releases all namespaces will be included in the backup with such settings. More details refer to #7105
|
||||
* Patching the PV in the "Finalizing" state may cause the restore to be in "PartiallyFailed" state when the PV is blocked in "Pending" state, while in the previous release the restore may end up being in "Complete" state. For more details refer to #7866
|
||||
|
||||
### All Changes
|
||||
* Fix backup log to show error string, not index (#7805, @piny940)
|
||||
* Modify the volume helper logic. (#7794, @blackpiglet)
|
||||
* Add documentation for extension of volume policy feature (#7779, @shubham-pampattiwar)
|
||||
* Surface errors when waiting for backupRepository and timeout occurs (#7762, @kaovilai)
|
||||
* Add existingResourcePolicy restore CR validation to controller (#7757, @kaovilai)
|
||||
* Fix condition matching in resource modifier when there are multiple rules (#7715, @27149chen)
|
||||
* Bump up the version of KinD and k8s in github actions (#7702, @reasonerjt)
|
||||
* Implementation for Extending VolumePolicies to support more actions (#7664, @shubham-pampattiwar)
|
||||
* Migrate from `github.com/Azure/azure-storage-blob-go` to `github.com/Azure/azure-sdk-for-go/sdk/storage/azblob` (#7598, @mmorel-35)
|
||||
* When Included/ExcludedNamespaces are omitted, and LabelSelector or OrLabelSelector is used, namespaces without selected items are excluded from backup. (#7697, @blackpiglet)
|
||||
* Display CSI snapshot restores in restore describe (#7687, @reasonerjt)
|
||||
* Use specific credential rather than the credential chain for Azure (#7680, @ywk253100)
|
||||
* Modify hook docs for clarity on displaying hook execution results (#7679, @allenxu404)
|
||||
* Wait for results of restore exec hook executions in Finalizing phase instead of InProgress phase (#7619, @allenxu404)
|
||||
* migrating to `sdk/resourcemanager/**/arm**` from `services/**/mgmt/**` (#7596, @mmorel-35)
|
||||
* Bump up to go1.22 (#7666, @reasonerjt)
|
||||
* Fix issue #7648. Adjust the exposing logic to avoid exposing failure and snapshot leak when expose fails (#7662, @Lyndon-Li)
|
||||
* Track and persist restore volume info (#7630, @reasonerjt)
|
||||
* Check the existence of the namespaces provided in the "--include-namespaces" option (#7569, @ywk253100)
|
||||
* Add the finalization phase to the restore workflow (#7377, @allenxu404)
|
||||
* Upgrade the version of go plugin related libs/tools (#7373, @ywk253100)
|
||||
* Check resource Group Version and Kind is available in cluster before attempting restore to prevent being stuck. (#7322, @kaovilai)
|
||||
* Merge CSI plugin code into Velero. (#7609, @blackpiglet)
|
||||
* Fix issue #7391, remove the default constraint for node-agent pods (#7488, @Lyndon-Li)
|
||||
* Fix DataDownload fails during restore for empty PVC workload (#7521, @qiuming-best)
|
||||
* Add repository maintenance job (#7451, @qiuming-best)
|
||||
* Check whether the VolumeSnapshot's source PVC is nil before using it.
|
||||
Skip populate VolumeInfo for data-moved PV when CSI is not enabled. (#7515, @blackpiglet)
|
||||
* Fix issue #7308, change the data path requeue time to 5 second for data mover backup/restore, PVB and PVR. (#7458, @Lyndon-Li)
|
||||
* Patch newly dynamically provisioned PV with volume info to restore custom setting of PV (#7504, @allenxu404)
|
||||
* Adjust the logic for the backup_last_status metrics to stop incorrectly incrementing over time (#7445, @allenxu404)
|
||||
* dependabot: support github-actions updates (#7594, @mmorel-35)
|
||||
* Include the design for adding the finalization phase to the restore workflow (#7317, @allenxu404)
|
||||
* Fix issue #7211. Enable advanced feature capability and add support to concatenate objects for unified repo. (#7452, @Lyndon-Li)
|
||||
* Add design to introduce restore volume info (#7610, @reasonerjt)
|
||||
* Increase the k8s client QPS/burst to avoid throttling request errors (#7311, @ywk253100)
|
||||
* Support update the backup VolumeInfos by the Async ops result. (#7554, @blackpiglet)
|
||||
* FS backup create PodVolumeBackup when the backup excluded PVC,
|
||||
so I added logic to skip PVC volume type when PVC is not included in the backup resources to be backed up. (#7472, @sbahar619)
|
||||
* Respect and use `credentialsFile` specified in BSL.spec.config when IRSA is configured over Velero Pod Environment credentials (#7374, @reasonerjt)
|
||||
* Move the native snapshot definition code into internal directory (#7544, @blackpiglet)
|
||||
* Fix issue #7036. Add the implementation of node selection for data mover backups (#7437, @Lyndon-Li)
|
||||
* Fix issue #7535, add the MustHave resource check during item collection and item filter for restore (#7585, @Lyndon-Li)
|
||||
* build(deps): bump json-patch to v5.8.0 (#7584, @mmorel-35)
|
||||
* Add confirm flag to velero plugin add (#7566, @kaovilai)
|
||||
* do not skip unknown gvr at the beginning and get new gr when kind is changed (#7523, @27149chen)
|
||||
* Fix snapshot leak for backup (#7558, @qiuming-best)
|
||||
* For issue #7036, add the document for data mover node selection (#7640, @Lyndon-Li)
|
||||
* Add design for Extending VolumePolicies to support more actions (#6956, @shubham-pampattiwar)
|
||||
* BackupRepositories associated with a BSL are invalidated when BSL is (re-)created. (#7380, @kaovilai)
|
||||
* Improve the concurrency for PVBs in different pods (#7571, @ywk253100)
|
||||
* Bump up Kopia to v0.16.0 and open kopia repo with no index change (#7559, @Lyndon-Li)
|
||||
* Bump up the versions of several Kubernetes-related libs (#7489, @ywk253100)
|
||||
* Make parallel restore configurable (#7512, @qiuming-best)
|
||||
* Support certificate-based authentication for Azure (#7549, @ywk253100)
|
||||
* Fix issue #7281, batch delete snapshots in the same repo (#7438, @Lyndon-Li)
|
||||
* Add CRD name to error message when it is not ready to use (#7295, @josemarevalo)
|
||||
* Add the design for node selection for data mover backup (#7383, @Lyndon-Li)
|
||||
* Bump up aws-sdk to latest version to leverage Pod Identity credentials. (#7307, @guikcd)
|
||||
* Fix issue #7246. Document the behavior for repo snapshot deletion (#7622, @Lyndon-Li)
|
||||
* Fix issue #7583, set backupName optional for Restore CRD (#7617, @Lyndon-Li)
|
||||
|
||||
@@ -1,19 +1,3 @@
|
||||
/*
|
||||
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 main
|
||||
|
||||
import (
|
||||
|
||||
@@ -66,14 +66,14 @@ func done() bool {
|
||||
doneFile := filepath.Join("/restores", child.Name(), ".velero", os.Args[1])
|
||||
|
||||
if _, err := os.Stat(doneFile); os.IsNotExist(err) {
|
||||
fmt.Printf("The filesystem restore done file %s is not found yet. Retry later.\n", doneFile)
|
||||
fmt.Printf("Not found: %s\n", doneFile)
|
||||
return false
|
||||
} else if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ERROR looking filesystem restore done file %s: %s\n", doneFile, err)
|
||||
fmt.Fprintf(os.Stderr, "ERROR looking for %s: %s\n", doneFile, err)
|
||||
return false
|
||||
}
|
||||
|
||||
fmt.Printf("Found the done file %s\n", doneFile)
|
||||
fmt.Printf("Found %s", doneFile)
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.12.0
|
||||
name: backuprepositories.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
@@ -26,19 +26,14 @@ spec:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -46,8 +41,7 @@ spec:
|
||||
description: BackupRepositorySpec is the specification for a BackupRepository.
|
||||
properties:
|
||||
backupStorageLocation:
|
||||
description: |-
|
||||
BackupStorageLocation is the name of the BackupStorageLocation
|
||||
description: BackupStorageLocation is the name of the BackupStorageLocation
|
||||
that should contain this repository.
|
||||
type: string
|
||||
maintenanceFrequency:
|
||||
@@ -62,14 +56,12 @@ spec:
|
||||
- ""
|
||||
type: string
|
||||
resticIdentifier:
|
||||
description: |-
|
||||
ResticIdentifier is the full restic-compatible string for identifying
|
||||
this repository.
|
||||
description: ResticIdentifier is the full restic-compatible string
|
||||
for identifying this repository.
|
||||
type: string
|
||||
volumeNamespace:
|
||||
description: |-
|
||||
VolumeNamespace is the namespace this backup repository contains
|
||||
pod volume backups for.
|
||||
description: VolumeNamespace is the namespace this backup repository
|
||||
contains pod volume backups for.
|
||||
type: string
|
||||
required:
|
||||
- backupStorageLocation
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.12.0
|
||||
name: backups.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
@@ -17,24 +17,18 @@ spec:
|
||||
- name: v1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: |-
|
||||
Backup is a Velero resource that represents the capture of Kubernetes
|
||||
description: Backup is a Velero resource that represents the capture of Kubernetes
|
||||
cluster state at a point in time (API objects and associated volume state).
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -42,63 +36,55 @@ spec:
|
||||
description: BackupSpec defines the specification for a Velero backup.
|
||||
properties:
|
||||
csiSnapshotTimeout:
|
||||
description: |-
|
||||
CSISnapshotTimeout specifies the time used to wait for CSI VolumeSnapshot status turns to
|
||||
ReadyToUse during creation, before returning error as timeout.
|
||||
The default value is 10 minute.
|
||||
description: CSISnapshotTimeout specifies the time used to wait for
|
||||
CSI VolumeSnapshot status turns to ReadyToUse during creation, before
|
||||
returning error as timeout. The default value is 10 minute.
|
||||
type: string
|
||||
datamover:
|
||||
description: |-
|
||||
DataMover specifies the data mover to be used by the backup.
|
||||
If DataMover is "" or "velero", the built-in data mover will be used.
|
||||
description: DataMover specifies the data mover to be used by the
|
||||
backup. If DataMover is "" or "velero", the built-in data mover
|
||||
will be used.
|
||||
type: string
|
||||
defaultVolumesToFsBackup:
|
||||
description: |-
|
||||
DefaultVolumesToFsBackup specifies whether pod volume file system backup should be used
|
||||
for all volumes by default.
|
||||
description: DefaultVolumesToFsBackup specifies whether pod volume
|
||||
file system backup should be used for all volumes by default.
|
||||
nullable: true
|
||||
type: boolean
|
||||
defaultVolumesToRestic:
|
||||
description: |-
|
||||
DefaultVolumesToRestic specifies whether restic should be used to take a
|
||||
backup of all pod volumes by default.
|
||||
|
||||
|
||||
Deprecated: this field is no longer used and will be removed entirely in future. Use DefaultVolumesToFsBackup instead.
|
||||
description: "DefaultVolumesToRestic specifies whether restic should
|
||||
be used to take a backup of all pod volumes by default. \n Deprecated:
|
||||
this field is no longer used and will be removed entirely in future.
|
||||
Use DefaultVolumesToFsBackup instead."
|
||||
nullable: true
|
||||
type: boolean
|
||||
excludedClusterScopedResources:
|
||||
description: |-
|
||||
ExcludedClusterScopedResources is a slice of cluster-scoped
|
||||
resource type names to exclude from the backup.
|
||||
If set to "*", all cluster-scoped resource types are excluded.
|
||||
The default value is empty.
|
||||
description: ExcludedClusterScopedResources is a slice of cluster-scoped
|
||||
resource type names to exclude from the backup. If set to "*", all
|
||||
cluster-scoped resource types are excluded. The default value is
|
||||
empty.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
excludedNamespaceScopedResources:
|
||||
description: |-
|
||||
ExcludedNamespaceScopedResources is a slice of namespace-scoped
|
||||
resource type names to exclude from the backup.
|
||||
If set to "*", all namespace-scoped resource types are excluded.
|
||||
The default value is empty.
|
||||
description: ExcludedNamespaceScopedResources is a slice of namespace-scoped
|
||||
resource type names to exclude from the backup. If set to "*", all
|
||||
namespace-scoped resource types are excluded. The default value
|
||||
is empty.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
excludedNamespaces:
|
||||
description: |-
|
||||
ExcludedNamespaces contains a list of namespaces that are not
|
||||
included in the backup.
|
||||
description: ExcludedNamespaces contains a list of namespaces that
|
||||
are not included in the backup.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
excludedResources:
|
||||
description: |-
|
||||
ExcludedResources is a slice of resource names that are not
|
||||
included in the backup.
|
||||
description: ExcludedResources is a slice of resource names that are
|
||||
not included in the backup.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
@@ -111,9 +97,9 @@ spec:
|
||||
description: Resources are hooks that should be executed when
|
||||
backing up individual instances of a resource.
|
||||
items:
|
||||
description: |-
|
||||
BackupResourceHookSpec defines one or more BackupResourceHooks that should be executed based on
|
||||
the rules defined for namespaces, resources, and label selector.
|
||||
description: BackupResourceHookSpec defines one or more BackupResourceHooks
|
||||
that should be executed based on the rules defined for namespaces,
|
||||
resources, and label selector.
|
||||
properties:
|
||||
excludedNamespaces:
|
||||
description: ExcludedNamespaces specifies the namespaces
|
||||
@@ -130,17 +116,17 @@ spec:
|
||||
nullable: true
|
||||
type: array
|
||||
includedNamespaces:
|
||||
description: |-
|
||||
IncludedNamespaces specifies the namespaces to which this hook spec applies. If empty, it applies
|
||||
description: IncludedNamespaces specifies the namespaces
|
||||
to which this hook spec applies. If empty, it applies
|
||||
to all namespaces.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedResources:
|
||||
description: |-
|
||||
IncludedResources specifies the resources to which this hook spec applies. If empty, it applies
|
||||
to all resources.
|
||||
description: IncludedResources specifies the resources to
|
||||
which this hook spec applies. If empty, it applies to
|
||||
all resources.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
@@ -154,8 +140,8 @@ spec:
|
||||
description: matchExpressions is a list of label selector
|
||||
requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
description: A label selector requirement is a selector
|
||||
that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
@@ -163,16 +149,17 @@ spec:
|
||||
applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
description: operator represents a key's relationship
|
||||
to a set of values. Valid operators are In,
|
||||
NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
description: values is an array of string values.
|
||||
If the operator is In or NotIn, the values array
|
||||
must be non-empty. If the operator is Exists
|
||||
or DoesNotExist, the values array must be empty.
|
||||
This array is replaced during a strategic merge
|
||||
patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -184,10 +171,11 @@ spec:
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
description: matchLabels is a map of {key,value} pairs.
|
||||
A single {key,value} in the matchLabels map is equivalent
|
||||
to an element of matchExpressions, whose key field
|
||||
is "key", the operator is "In", and the values array
|
||||
contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
@@ -195,9 +183,10 @@ spec:
|
||||
description: Name is the name of this hook.
|
||||
type: string
|
||||
post:
|
||||
description: |-
|
||||
PostHooks is a list of BackupResourceHooks to execute after storing the item in the backup.
|
||||
These are executed after all "additional items" from item actions are processed.
|
||||
description: PostHooks is a list of BackupResourceHooks
|
||||
to execute after storing the item in the backup. These
|
||||
are executed after all "additional items" from item actions
|
||||
are processed.
|
||||
items:
|
||||
description: BackupResourceHook defines a hook for a resource.
|
||||
properties:
|
||||
@@ -212,9 +201,10 @@ spec:
|
||||
minItems: 1
|
||||
type: array
|
||||
container:
|
||||
description: |-
|
||||
Container is the container in the pod where the command should be executed. If not specified,
|
||||
the pod's first container is used.
|
||||
description: Container is the container in the
|
||||
pod where the command should be executed. If
|
||||
not specified, the pod's first container is
|
||||
used.
|
||||
type: string
|
||||
onError:
|
||||
description: OnError specifies how Velero should
|
||||
@@ -225,9 +215,9 @@ spec:
|
||||
- Fail
|
||||
type: string
|
||||
timeout:
|
||||
description: |-
|
||||
Timeout defines the maximum amount of time Velero should wait for the hook to complete before
|
||||
considering the execution a failure.
|
||||
description: Timeout defines the maximum amount
|
||||
of time Velero should wait for the hook to complete
|
||||
before considering the execution a failure.
|
||||
type: string
|
||||
required:
|
||||
- command
|
||||
@@ -237,9 +227,10 @@ spec:
|
||||
type: object
|
||||
type: array
|
||||
pre:
|
||||
description: |-
|
||||
PreHooks is a list of BackupResourceHooks to execute prior to storing the item in the backup.
|
||||
These are executed before any "additional items" from item actions are processed.
|
||||
description: PreHooks is a list of BackupResourceHooks to
|
||||
execute prior to storing the item in the backup. These
|
||||
are executed before any "additional items" from item actions
|
||||
are processed.
|
||||
items:
|
||||
description: BackupResourceHook defines a hook for a resource.
|
||||
properties:
|
||||
@@ -254,9 +245,10 @@ spec:
|
||||
minItems: 1
|
||||
type: array
|
||||
container:
|
||||
description: |-
|
||||
Container is the container in the pod where the command should be executed. If not specified,
|
||||
the pod's first container is used.
|
||||
description: Container is the container in the
|
||||
pod where the command should be executed. If
|
||||
not specified, the pod's first container is
|
||||
used.
|
||||
type: string
|
||||
onError:
|
||||
description: OnError specifies how Velero should
|
||||
@@ -267,9 +259,9 @@ spec:
|
||||
- Fail
|
||||
type: string
|
||||
timeout:
|
||||
description: |-
|
||||
Timeout defines the maximum amount of time Velero should wait for the hook to complete before
|
||||
considering the execution a failure.
|
||||
description: Timeout defines the maximum amount
|
||||
of time Velero should wait for the hook to complete
|
||||
before considering the execution a failure.
|
||||
type: string
|
||||
required:
|
||||
- command
|
||||
@@ -285,81 +277,74 @@ spec:
|
||||
type: array
|
||||
type: object
|
||||
includeClusterResources:
|
||||
description: |-
|
||||
IncludeClusterResources specifies whether cluster-scoped resources
|
||||
should be included for consideration in the backup.
|
||||
description: IncludeClusterResources specifies whether cluster-scoped
|
||||
resources should be included for consideration in the backup.
|
||||
nullable: true
|
||||
type: boolean
|
||||
includedClusterScopedResources:
|
||||
description: |-
|
||||
IncludedClusterScopedResources is a slice of cluster-scoped
|
||||
resource type names to include in the backup.
|
||||
If set to "*", all cluster-scoped resource types are included.
|
||||
The default value is empty, which means only related
|
||||
cluster-scoped resources are included.
|
||||
description: IncludedClusterScopedResources is a slice of cluster-scoped
|
||||
resource type names to include in the backup. If set to "*", all
|
||||
cluster-scoped resource types are included. The default value is
|
||||
empty, which means only related cluster-scoped resources are included.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedNamespaceScopedResources:
|
||||
description: |-
|
||||
IncludedNamespaceScopedResources is a slice of namespace-scoped
|
||||
resource type names to include in the backup.
|
||||
The default value is "*".
|
||||
description: IncludedNamespaceScopedResources is a slice of namespace-scoped
|
||||
resource type names to include in the backup. The default value
|
||||
is "*".
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedNamespaces:
|
||||
description: |-
|
||||
IncludedNamespaces is a slice of namespace names to include objects
|
||||
from. If empty, all namespaces are included.
|
||||
description: IncludedNamespaces is a slice of namespace names to include
|
||||
objects from. If empty, all namespaces are included.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedResources:
|
||||
description: |-
|
||||
IncludedResources is a slice of resource names to include
|
||||
description: IncludedResources is a slice of resource names to include
|
||||
in the backup. If empty, all resources are included.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
itemOperationTimeout:
|
||||
description: |-
|
||||
ItemOperationTimeout specifies the time used to wait for asynchronous BackupItemAction operations
|
||||
The default value is 4 hour.
|
||||
description: ItemOperationTimeout specifies the time used to wait
|
||||
for asynchronous BackupItemAction operations The default value is
|
||||
1 hour.
|
||||
type: string
|
||||
labelSelector:
|
||||
description: |-
|
||||
LabelSelector is a metav1.LabelSelector to filter with
|
||||
when adding individual objects to the backup. If empty
|
||||
or nil, all objects are included. Optional.
|
||||
description: LabelSelector is a metav1.LabelSelector to filter with
|
||||
when adding individual objects to the backup. If empty or nil, all
|
||||
objects are included. Optional.
|
||||
nullable: true
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
description: A label selector requirement is a selector that
|
||||
contains values, a key, and an operator that relates the key
|
||||
and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
description: operator represents a key's relationship to
|
||||
a set of values. Valid operators are In, NotIn, Exists
|
||||
and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
description: values is an array of string values. If the
|
||||
operator is In or NotIn, the values array must be non-empty.
|
||||
If the operator is Exists or DoesNotExist, the values
|
||||
array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
@@ -372,10 +357,11 @@ spec:
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
description: matchLabels is a map of {key,value} pairs. A single
|
||||
{key,value} in the matchLabels map is equivalent to an element
|
||||
of matchExpressions, whose key field is "key", the operator
|
||||
is "In", and the values array contains only "value". The requirements
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
@@ -387,41 +373,40 @@ spec:
|
||||
type: object
|
||||
type: object
|
||||
orLabelSelectors:
|
||||
description: |-
|
||||
OrLabelSelectors is list of metav1.LabelSelector to filter with
|
||||
when adding individual objects to the backup. If multiple provided
|
||||
description: OrLabelSelectors is list of metav1.LabelSelector to filter
|
||||
with when adding individual objects to the backup. If multiple provided
|
||||
they will be joined by the OR operator. LabelSelector as well as
|
||||
OrLabelSelectors cannot co-exist in backup request, only one of them
|
||||
can be used.
|
||||
OrLabelSelectors cannot co-exist in backup request, only one of
|
||||
them can be used.
|
||||
items:
|
||||
description: |-
|
||||
A label selector is a label query over a set of resources. The result of matchLabels and
|
||||
matchExpressions are ANDed. An empty label selector matches all objects. A null
|
||||
label selector matches no objects.
|
||||
description: A label selector is a label query over a set of resources.
|
||||
The result of matchLabels and matchExpressions are ANDed. An empty
|
||||
label selector matches all objects. A null label selector matches
|
||||
no objects.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
description: A label selector requirement is a selector that
|
||||
contains values, a key, and an operator that relates the
|
||||
key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
description: operator represents a key's relationship
|
||||
to a set of values. Valid operators are In, NotIn, Exists
|
||||
and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
description: values is an array of string values. If the
|
||||
operator is In or NotIn, the values array must be non-empty.
|
||||
If the operator is Exists or DoesNotExist, the values
|
||||
array must be empty. This array is replaced during a
|
||||
strategic merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -433,10 +418,11 @@ spec:
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
description: matchLabels is a map of {key,value} pairs. A single
|
||||
{key,value} in the matchLabels map is equivalent to an element
|
||||
of matchExpressions, whose key field is "key", the operator
|
||||
is "In", and the values array contains only "value". The requirements
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
@@ -445,10 +431,11 @@ spec:
|
||||
orderedResources:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
OrderedResources specifies the backup order of resources of specific Kind.
|
||||
The map key is the resource name and value is a list of object names separated by commas.
|
||||
Each resource name has format "namespace/objectname". For cluster resources, simply use "objectname".
|
||||
description: OrderedResources specifies the backup order of resources
|
||||
of specific Kind. The map key is the resource name and value is
|
||||
a list of object names separated by commas. Each resource name has
|
||||
format "namespace/objectname". For cluster resources, simply use
|
||||
"objectname".
|
||||
nullable: true
|
||||
type: object
|
||||
resourcePolicy:
|
||||
@@ -456,10 +443,10 @@ spec:
|
||||
that backup should follow
|
||||
properties:
|
||||
apiGroup:
|
||||
description: |-
|
||||
APIGroup is the group for the resource being referenced.
|
||||
If APIGroup is not specified, the specified Kind must be in the core API group.
|
||||
For any other third-party types, APIGroup is required.
|
||||
description: APIGroup is the group for the resource being referenced.
|
||||
If APIGroup is not specified, the specified Kind must be in
|
||||
the core API group. For any other third-party types, APIGroup
|
||||
is required.
|
||||
type: string
|
||||
kind:
|
||||
description: Kind is the type of resource being referenced
|
||||
@@ -478,10 +465,8 @@ spec:
|
||||
nullable: true
|
||||
type: boolean
|
||||
snapshotVolumes:
|
||||
description: |-
|
||||
SnapshotVolumes specifies whether to take snapshots
|
||||
of any PV's referenced in the set of objects included
|
||||
in the Backup.
|
||||
description: SnapshotVolumes specifies whether to take snapshots of
|
||||
any PV's referenced in the set of objects included in the Backup.
|
||||
nullable: true
|
||||
type: boolean
|
||||
storageLocation:
|
||||
@@ -489,19 +474,9 @@ spec:
|
||||
BackupStorageLocation where the backup should be stored.
|
||||
type: string
|
||||
ttl:
|
||||
description: |-
|
||||
TTL is a time.Duration-parseable string describing how long
|
||||
the Backup should be retained for.
|
||||
description: TTL is a time.Duration-parseable string describing how
|
||||
long the Backup should be retained for.
|
||||
type: string
|
||||
uploaderConfig:
|
||||
description: UploaderConfig specifies the configuration for the uploader.
|
||||
nullable: true
|
||||
properties:
|
||||
parallelFilesUpload:
|
||||
description: ParallelFilesUpload is the number of files parallel
|
||||
uploads to perform when using the uploader.
|
||||
type: integer
|
||||
type: object
|
||||
volumeSnapshotLocations:
|
||||
description: VolumeSnapshotLocations is a list containing names of
|
||||
VolumeSnapshotLocations associated with this backup.
|
||||
@@ -513,44 +488,39 @@ spec:
|
||||
description: BackupStatus captures the current status of a Velero backup.
|
||||
properties:
|
||||
backupItemOperationsAttempted:
|
||||
description: |-
|
||||
BackupItemOperationsAttempted is the total number of attempted
|
||||
async BackupItemAction operations for this backup.
|
||||
description: BackupItemOperationsAttempted is the total number of
|
||||
attempted async BackupItemAction operations for this backup.
|
||||
type: integer
|
||||
backupItemOperationsCompleted:
|
||||
description: |-
|
||||
BackupItemOperationsCompleted is the total number of successfully completed
|
||||
async BackupItemAction operations for this backup.
|
||||
description: BackupItemOperationsCompleted is the total number of
|
||||
successfully completed async BackupItemAction operations for this
|
||||
backup.
|
||||
type: integer
|
||||
backupItemOperationsFailed:
|
||||
description: |-
|
||||
BackupItemOperationsFailed is the total number of async
|
||||
BackupItemAction operations for this backup which ended with an error.
|
||||
description: BackupItemOperationsFailed is the total number of async
|
||||
BackupItemAction operations for this backup which ended with an
|
||||
error.
|
||||
type: integer
|
||||
completionTimestamp:
|
||||
description: |-
|
||||
CompletionTimestamp records the time a backup was completed.
|
||||
Completion time is recorded even on failed backups.
|
||||
Completion time is recorded before uploading the backup object.
|
||||
The server's time is used for CompletionTimestamps
|
||||
description: CompletionTimestamp records the time a backup was completed.
|
||||
Completion time is recorded even on failed backups. Completion time
|
||||
is recorded before uploading the backup object. The server's time
|
||||
is used for CompletionTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
csiVolumeSnapshotsAttempted:
|
||||
description: |-
|
||||
CSIVolumeSnapshotsAttempted is the total number of attempted
|
||||
description: CSIVolumeSnapshotsAttempted is the total number of attempted
|
||||
CSI VolumeSnapshots for this backup.
|
||||
type: integer
|
||||
csiVolumeSnapshotsCompleted:
|
||||
description: |-
|
||||
CSIVolumeSnapshotsCompleted is the total number of successfully
|
||||
description: CSIVolumeSnapshotsCompleted is the total number of successfully
|
||||
completed CSI VolumeSnapshots for this backup.
|
||||
type: integer
|
||||
errors:
|
||||
description: |-
|
||||
Errors is a count of all error messages that were generated during
|
||||
execution of the backup. The actual errors are in the backup's log
|
||||
file in object storage.
|
||||
description: Errors is a count of all error messages that were generated
|
||||
during execution of the backup. The actual errors are in the backup's
|
||||
log file in object storage.
|
||||
type: integer
|
||||
expiration:
|
||||
description: Expiration is when this Backup is eligible for garbage-collection.
|
||||
@@ -565,22 +535,6 @@ spec:
|
||||
description: FormatVersion is the backup format version, including
|
||||
major, minor, and patch version.
|
||||
type: string
|
||||
hookStatus:
|
||||
description: HookStatus contains information about the status of the
|
||||
hooks.
|
||||
nullable: true
|
||||
properties:
|
||||
hooksAttempted:
|
||||
description: |-
|
||||
HooksAttempted is the total number of attempted hooks
|
||||
Specifically, HooksAttempted represents the number of hooks that failed to execute
|
||||
and the number of hooks that executed successfully.
|
||||
type: integer
|
||||
hooksFailed:
|
||||
description: HooksFailed is the total number of hooks which ended
|
||||
with an error
|
||||
type: integer
|
||||
type: object
|
||||
phase:
|
||||
description: Phase is the current state of the Backup.
|
||||
enum:
|
||||
@@ -597,62 +551,53 @@ spec:
|
||||
- Deleting
|
||||
type: string
|
||||
progress:
|
||||
description: |-
|
||||
Progress contains information about the backup's execution progress. Note
|
||||
that this information is best-effort only -- if Velero fails to update it
|
||||
during a backup for any reason, it may be inaccurate/stale.
|
||||
description: Progress contains information about the backup's execution
|
||||
progress. Note that this information is best-effort only -- if Velero
|
||||
fails to update it during a backup for any reason, it may be inaccurate/stale.
|
||||
nullable: true
|
||||
properties:
|
||||
itemsBackedUp:
|
||||
description: |-
|
||||
ItemsBackedUp is the number of items that have actually been written to the
|
||||
backup tarball so far.
|
||||
description: ItemsBackedUp is the number of items that have actually
|
||||
been written to the backup tarball so far.
|
||||
type: integer
|
||||
totalItems:
|
||||
description: |-
|
||||
TotalItems is the total number of items to be backed up. This number may change
|
||||
throughout the execution of the backup due to plugins that return additional related
|
||||
items to back up, the velero.io/exclude-from-backup label, and various other
|
||||
description: TotalItems is the total number of items to be backed
|
||||
up. This number may change throughout the execution of the backup
|
||||
due to plugins that return additional related items to back
|
||||
up, the velero.io/exclude-from-backup label, and various other
|
||||
filters that happen as items are processed.
|
||||
type: integer
|
||||
type: object
|
||||
startTimestamp:
|
||||
description: |-
|
||||
StartTimestamp records the time a backup was started.
|
||||
Separate from CreationTimestamp, since that value changes
|
||||
on restores.
|
||||
description: StartTimestamp records the time a backup was started.
|
||||
Separate from CreationTimestamp, since that value changes on restores.
|
||||
The server's time is used for StartTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
validationErrors:
|
||||
description: |-
|
||||
ValidationErrors is a slice of all validation errors (if
|
||||
applicable).
|
||||
description: ValidationErrors is a slice of all validation errors
|
||||
(if applicable).
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
version:
|
||||
description: |-
|
||||
Version is the backup format major version.
|
||||
Deprecated: Please see FormatVersion
|
||||
description: 'Version is the backup format major version. Deprecated:
|
||||
Please see FormatVersion'
|
||||
type: integer
|
||||
volumeSnapshotsAttempted:
|
||||
description: |-
|
||||
VolumeSnapshotsAttempted is the total number of attempted
|
||||
description: VolumeSnapshotsAttempted is the total number of attempted
|
||||
volume snapshots for this backup.
|
||||
type: integer
|
||||
volumeSnapshotsCompleted:
|
||||
description: |-
|
||||
VolumeSnapshotsCompleted is the total number of successfully
|
||||
description: VolumeSnapshotsCompleted is the total number of successfully
|
||||
completed volume snapshots for this backup.
|
||||
type: integer
|
||||
warnings:
|
||||
description: |-
|
||||
Warnings is a count of all warning messages that were generated during
|
||||
execution of the backup. The actual warnings are in the backup's log
|
||||
file in object storage.
|
||||
description: Warnings is a count of all warning messages that were
|
||||
generated during execution of the backup. The actual warnings are
|
||||
in the backup's log file in object storage.
|
||||
type: integer
|
||||
type: object
|
||||
type: object
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.12.0
|
||||
name: backupstoragelocations.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
@@ -40,19 +40,14 @@ spec:
|
||||
objects
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -86,10 +81,8 @@ spec:
|
||||
valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
Name of the referent.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||
type: string
|
||||
optional:
|
||||
description: Specify whether the Secret or its key must be defined
|
||||
@@ -138,36 +131,29 @@ spec:
|
||||
BackupStorageLocation
|
||||
properties:
|
||||
accessMode:
|
||||
description: |-
|
||||
AccessMode is an unused field.
|
||||
|
||||
|
||||
Deprecated: there is now an AccessMode field on the Spec and this field
|
||||
will be removed entirely as of v2.0.
|
||||
description: "AccessMode is an unused field. \n Deprecated: there
|
||||
is now an AccessMode field on the Spec and this field will be removed
|
||||
entirely as of v2.0."
|
||||
enum:
|
||||
- ReadOnly
|
||||
- ReadWrite
|
||||
type: string
|
||||
lastSyncedRevision:
|
||||
description: |-
|
||||
LastSyncedRevision is the value of the `metadata/revision` file in the backup
|
||||
storage location the last time the BSL's contents were synced into the cluster.
|
||||
|
||||
|
||||
Deprecated: this field is no longer updated or used for detecting changes to
|
||||
the location's contents and will be removed entirely in v2.0.
|
||||
description: "LastSyncedRevision is the value of the `metadata/revision`
|
||||
file in the backup storage location the last time the BSL's contents
|
||||
were synced into the cluster. \n Deprecated: this field is no longer
|
||||
updated or used for detecting changes to the location's contents
|
||||
and will be removed entirely in v2.0."
|
||||
type: string
|
||||
lastSyncedTime:
|
||||
description: |-
|
||||
LastSyncedTime is the last time the contents of the location were synced into
|
||||
the cluster.
|
||||
description: LastSyncedTime is the last time the contents of the location
|
||||
were synced into the cluster.
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
lastValidationTime:
|
||||
description: |-
|
||||
LastValidationTime is the last time the backup store location was validated
|
||||
the cluster.
|
||||
description: LastValidationTime is the last time the backup store
|
||||
location was validated the cluster.
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.12.0
|
||||
name: deletebackuprequests.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
@@ -29,19 +29,14 @@ spec:
|
||||
description: DeleteBackupRequest is a request to delete one or more backups.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.12.0
|
||||
name: downloadrequests.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
@@ -17,24 +17,18 @@ spec:
|
||||
- name: v1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: |-
|
||||
DownloadRequest is a request to download an artifact from backup object storage, such as a backup
|
||||
log file.
|
||||
description: DownloadRequest is a request to download an artifact from backup
|
||||
object storage, such as a backup log file.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -59,8 +53,6 @@ spec:
|
||||
- RestoreItemOperations
|
||||
- CSIBackupVolumeSnapshots
|
||||
- CSIBackupVolumeSnapshotContents
|
||||
- BackupVolumeInfos
|
||||
- RestoreVolumeInfo
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the Kubernetes resource with
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.12.0
|
||||
name: podvolumebackups.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
@@ -35,6 +35,10 @@ spec:
|
||||
jsonPath: .spec.volume
|
||||
name: Volume
|
||||
type: string
|
||||
- description: Backup repository identifier for this backup
|
||||
jsonPath: .spec.repoIdentifier
|
||||
name: Repository ID
|
||||
type: string
|
||||
- description: The type of the uploader to handle data transfer
|
||||
jsonPath: .spec.uploaderType
|
||||
name: Uploader Type
|
||||
@@ -52,19 +56,14 @@ spec:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -72,9 +71,8 @@ spec:
|
||||
description: PodVolumeBackupSpec is the specification for a PodVolumeBackup.
|
||||
properties:
|
||||
backupStorageLocation:
|
||||
description: |-
|
||||
BackupStorageLocation is the name of the backup storage location
|
||||
where the backup repository is stored.
|
||||
description: BackupStorageLocation is the name of the backup storage
|
||||
location where the backup repository is stored.
|
||||
type: string
|
||||
node:
|
||||
description: Node is the name of the node that the Pod is running
|
||||
@@ -88,40 +86,33 @@ spec:
|
||||
description: API version of the referent.
|
||||
type: string
|
||||
fieldPath:
|
||||
description: |-
|
||||
If referring to a piece of an object instead of an entire object, this string
|
||||
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
|
||||
For example, if the object reference is to a container within a pod, this would take on a value like:
|
||||
"spec.containers{name}" (where "name" refers to the name of the container that triggered
|
||||
the event) or if no container name is specified "spec.containers[2]" (container with
|
||||
index 2 in this pod). This syntax is chosen only to have some well-defined way of
|
||||
referencing a part of an object.
|
||||
TODO: this design is not final and this field is subject to change in the future.
|
||||
description: 'If referring to a piece of an object instead of
|
||||
an entire object, this string should contain a valid JSON/Go
|
||||
field access statement, such as desiredState.manifest.containers[2].
|
||||
For example, if the object reference is to a container within
|
||||
a pod, this would take on a value like: "spec.containers{name}"
|
||||
(where "name" refers to the name of the container that triggered
|
||||
the event) or if no container name is specified "spec.containers[2]"
|
||||
(container with index 2 in this pod). This syntax is chosen
|
||||
only to have some well-defined way of referencing a part of
|
||||
an object. TODO: this design is not final and this field is
|
||||
subject to change in the future.'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind of the referent.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
Name of the referent.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
|
||||
type: string
|
||||
namespace:
|
||||
description: |-
|
||||
Namespace of the referent.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
|
||||
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
|
||||
type: string
|
||||
resourceVersion:
|
||||
description: |-
|
||||
Specific resourceVersion to which this reference is made, if any.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||
description: 'Specific resourceVersion to which this reference
|
||||
is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
|
||||
type: string
|
||||
uid:
|
||||
description: |-
|
||||
UID of the referent.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
|
||||
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
@@ -131,17 +122,8 @@ spec:
|
||||
tags:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
Tags are a map of key-value pairs that should be applied to the
|
||||
volume backup as tags.
|
||||
type: object
|
||||
uploaderSettings:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
UploaderSettings are a map of key-value pairs that should be applied to the
|
||||
uploader configuration.
|
||||
nullable: true
|
||||
description: Tags are a map of key-value pairs that should be applied
|
||||
to the volume backup as tags.
|
||||
type: object
|
||||
uploaderType:
|
||||
description: UploaderType is the type of the uploader to handle the
|
||||
@@ -152,9 +134,8 @@ spec:
|
||||
- ""
|
||||
type: string
|
||||
volume:
|
||||
description: |-
|
||||
Volume is the name of the volume within the Pod to be backed
|
||||
up.
|
||||
description: Volume is the name of the volume within the Pod to be
|
||||
backed up.
|
||||
type: string
|
||||
required:
|
||||
- backupStorageLocation
|
||||
@@ -167,11 +148,10 @@ spec:
|
||||
description: PodVolumeBackupStatus is the current status of a PodVolumeBackup.
|
||||
properties:
|
||||
completionTimestamp:
|
||||
description: |-
|
||||
CompletionTimestamp records the time a backup was completed.
|
||||
Completion time is recorded even on failed backups.
|
||||
Completion time is recorded before uploading the backup object.
|
||||
The server's time is used for CompletionTimestamps
|
||||
description: CompletionTimestamp records the time a backup was completed.
|
||||
Completion time is recorded even on failed backups. Completion time
|
||||
is recorded before uploading the backup object. The server's time
|
||||
is used for CompletionTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
@@ -191,10 +171,9 @@ spec:
|
||||
- Failed
|
||||
type: string
|
||||
progress:
|
||||
description: |-
|
||||
Progress holds the total number of bytes of the volume and the current
|
||||
number of backed up bytes. This can be used to display progress information
|
||||
about the backup operation.
|
||||
description: Progress holds the total number of bytes of the volume
|
||||
and the current number of backed up bytes. This can be used to display
|
||||
progress information about the backup operation.
|
||||
properties:
|
||||
bytesDone:
|
||||
format: int64
|
||||
@@ -208,10 +187,8 @@ spec:
|
||||
pod volume.
|
||||
type: string
|
||||
startTimestamp:
|
||||
description: |-
|
||||
StartTimestamp records the time a backup was started.
|
||||
Separate from CreationTimestamp, since that value changes
|
||||
on restores.
|
||||
description: StartTimestamp records the time a backup was started.
|
||||
Separate from CreationTimestamp, since that value changes on restores.
|
||||
The server's time is used for StartTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.12.0
|
||||
name: podvolumerestores.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
@@ -53,19 +53,14 @@ spec:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -73,9 +68,8 @@ spec:
|
||||
description: PodVolumeRestoreSpec is the specification for a PodVolumeRestore.
|
||||
properties:
|
||||
backupStorageLocation:
|
||||
description: |-
|
||||
BackupStorageLocation is the name of the backup storage location
|
||||
where the backup repository is stored.
|
||||
description: BackupStorageLocation is the name of the backup storage
|
||||
location where the backup repository is stored.
|
||||
type: string
|
||||
pod:
|
||||
description: Pod is a reference to the pod containing the volume to
|
||||
@@ -85,40 +79,33 @@ spec:
|
||||
description: API version of the referent.
|
||||
type: string
|
||||
fieldPath:
|
||||
description: |-
|
||||
If referring to a piece of an object instead of an entire object, this string
|
||||
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
|
||||
For example, if the object reference is to a container within a pod, this would take on a value like:
|
||||
"spec.containers{name}" (where "name" refers to the name of the container that triggered
|
||||
the event) or if no container name is specified "spec.containers[2]" (container with
|
||||
index 2 in this pod). This syntax is chosen only to have some well-defined way of
|
||||
referencing a part of an object.
|
||||
TODO: this design is not final and this field is subject to change in the future.
|
||||
description: 'If referring to a piece of an object instead of
|
||||
an entire object, this string should contain a valid JSON/Go
|
||||
field access statement, such as desiredState.manifest.containers[2].
|
||||
For example, if the object reference is to a container within
|
||||
a pod, this would take on a value like: "spec.containers{name}"
|
||||
(where "name" refers to the name of the container that triggered
|
||||
the event) or if no container name is specified "spec.containers[2]"
|
||||
(container with index 2 in this pod). This syntax is chosen
|
||||
only to have some well-defined way of referencing a part of
|
||||
an object. TODO: this design is not final and this field is
|
||||
subject to change in the future.'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind of the referent.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
Name of the referent.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
|
||||
type: string
|
||||
namespace:
|
||||
description: |-
|
||||
Namespace of the referent.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
|
||||
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
|
||||
type: string
|
||||
resourceVersion:
|
||||
description: |-
|
||||
Specific resourceVersion to which this reference is made, if any.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||
description: 'Specific resourceVersion to which this reference
|
||||
is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
|
||||
type: string
|
||||
uid:
|
||||
description: |-
|
||||
UID of the referent.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
|
||||
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
@@ -132,14 +119,6 @@ spec:
|
||||
description: SourceNamespace is the original namespace for namaspace
|
||||
mapping.
|
||||
type: string
|
||||
uploaderSettings:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
UploaderSettings are a map of key-value pairs that should be applied to the
|
||||
uploader configuration.
|
||||
nullable: true
|
||||
type: object
|
||||
uploaderType:
|
||||
description: UploaderType is the type of the uploader to handle the
|
||||
data transfer.
|
||||
@@ -164,10 +143,9 @@ spec:
|
||||
description: PodVolumeRestoreStatus is the current status of a PodVolumeRestore.
|
||||
properties:
|
||||
completionTimestamp:
|
||||
description: |-
|
||||
CompletionTimestamp records the time a restore was completed.
|
||||
Completion time is recorded even on failed restores.
|
||||
The server's time is used for CompletionTimestamps
|
||||
description: CompletionTimestamp records the time a restore was completed.
|
||||
Completion time is recorded even on failed restores. The server's
|
||||
time is used for CompletionTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
@@ -183,10 +161,9 @@ spec:
|
||||
- Failed
|
||||
type: string
|
||||
progress:
|
||||
description: |-
|
||||
Progress holds the total number of bytes of the snapshot and the current
|
||||
number of restored bytes. This can be used to display progress information
|
||||
about the restore operation.
|
||||
description: Progress holds the total number of bytes of the snapshot
|
||||
and the current number of restored bytes. This can be used to display
|
||||
progress information about the restore operation.
|
||||
properties:
|
||||
bytesDone:
|
||||
format: int64
|
||||
@@ -196,8 +173,7 @@ spec:
|
||||
type: integer
|
||||
type: object
|
||||
startTimestamp:
|
||||
description: |-
|
||||
StartTimestamp records the time a restore was started.
|
||||
description: StartTimestamp records the time a restore was started.
|
||||
The server's time is used for StartTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.12.0
|
||||
name: restores.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
@@ -17,24 +17,18 @@ spec:
|
||||
- name: v1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: |-
|
||||
Restore is a Velero resource that represents the application of
|
||||
resources from a Velero backup to a target Kubernetes cluster.
|
||||
description: Restore is a Velero resource that represents the application
|
||||
of resources from a Velero backup to a target Kubernetes cluster.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -42,22 +36,19 @@ spec:
|
||||
description: RestoreSpec defines the specification for a Velero restore.
|
||||
properties:
|
||||
backupName:
|
||||
description: |-
|
||||
BackupName is the unique name of the Velero backup to restore
|
||||
from.
|
||||
description: BackupName is the unique name of the Velero backup to
|
||||
restore from.
|
||||
type: string
|
||||
excludedNamespaces:
|
||||
description: |-
|
||||
ExcludedNamespaces contains a list of namespaces that are not
|
||||
included in the restore.
|
||||
description: ExcludedNamespaces contains a list of namespaces that
|
||||
are not included in the restore.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
excludedResources:
|
||||
description: |-
|
||||
ExcludedResources is a slice of resource names that are not
|
||||
included in the restore.
|
||||
description: ExcludedResources is a slice of resource names that are
|
||||
not included in the restore.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
@@ -73,9 +64,9 @@ spec:
|
||||
properties:
|
||||
resources:
|
||||
items:
|
||||
description: |-
|
||||
RestoreResourceHookSpec defines one or more RestoreResrouceHooks that should be executed based on
|
||||
the rules defined for namespaces, resources, and label selector.
|
||||
description: RestoreResourceHookSpec defines one or more RestoreResrouceHooks
|
||||
that should be executed based on the rules defined for namespaces,
|
||||
resources, and label selector.
|
||||
properties:
|
||||
excludedNamespaces:
|
||||
description: ExcludedNamespaces specifies the namespaces
|
||||
@@ -92,17 +83,17 @@ spec:
|
||||
nullable: true
|
||||
type: array
|
||||
includedNamespaces:
|
||||
description: |-
|
||||
IncludedNamespaces specifies the namespaces to which this hook spec applies. If empty, it applies
|
||||
description: IncludedNamespaces specifies the namespaces
|
||||
to which this hook spec applies. If empty, it applies
|
||||
to all namespaces.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedResources:
|
||||
description: |-
|
||||
IncludedResources specifies the resources to which this hook spec applies. If empty, it applies
|
||||
to all resources.
|
||||
description: IncludedResources specifies the resources to
|
||||
which this hook spec applies. If empty, it applies to
|
||||
all resources.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
@@ -116,8 +107,8 @@ spec:
|
||||
description: matchExpressions is a list of label selector
|
||||
requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
description: A label selector requirement is a selector
|
||||
that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
@@ -125,16 +116,17 @@ spec:
|
||||
applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
description: operator represents a key's relationship
|
||||
to a set of values. Valid operators are In,
|
||||
NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
description: values is an array of string values.
|
||||
If the operator is In or NotIn, the values array
|
||||
must be non-empty. If the operator is Exists
|
||||
or DoesNotExist, the values array must be empty.
|
||||
This array is replaced during a strategic merge
|
||||
patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -146,10 +138,11 @@ spec:
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
description: matchLabels is a map of {key,value} pairs.
|
||||
A single {key,value} in the matchLabels map is equivalent
|
||||
to an element of matchExpressions, whose key field
|
||||
is "key", the operator is "In", and the values array
|
||||
contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
@@ -175,14 +168,15 @@ spec:
|
||||
minItems: 1
|
||||
type: array
|
||||
container:
|
||||
description: |-
|
||||
Container is the container in the pod where the command should be executed. If not specified,
|
||||
the pod's first container is used.
|
||||
description: Container is the container in the
|
||||
pod where the command should be executed. If
|
||||
not specified, the pod's first container is
|
||||
used.
|
||||
type: string
|
||||
execTimeout:
|
||||
description: |-
|
||||
ExecTimeout defines the maximum amount of time Velero should wait for the hook to complete before
|
||||
considering the execution a failure.
|
||||
description: ExecTimeout defines the maximum amount
|
||||
of time Velero should wait for the hook to complete
|
||||
before considering the execution a failure.
|
||||
type: string
|
||||
onError:
|
||||
description: OnError specifies how Velero should
|
||||
@@ -192,16 +186,10 @@ spec:
|
||||
- Continue
|
||||
- Fail
|
||||
type: string
|
||||
waitForReady:
|
||||
description: WaitForReady ensures command will
|
||||
be launched when container is Ready instead
|
||||
of Running.
|
||||
nullable: true
|
||||
type: boolean
|
||||
waitTimeout:
|
||||
description: |-
|
||||
WaitTimeout defines the maximum amount of time Velero should wait for the container to be Ready
|
||||
before attempting to run the command.
|
||||
description: WaitTimeout defines the maximum amount
|
||||
of time Velero should wait for the container
|
||||
to be Ready before attempting to run the command.
|
||||
type: string
|
||||
required:
|
||||
- command
|
||||
@@ -231,62 +219,57 @@ spec:
|
||||
type: array
|
||||
type: object
|
||||
includeClusterResources:
|
||||
description: |-
|
||||
IncludeClusterResources specifies whether cluster-scoped resources
|
||||
should be included for consideration in the restore. If null, defaults
|
||||
to true.
|
||||
description: IncludeClusterResources specifies whether cluster-scoped
|
||||
resources should be included for consideration in the restore. If
|
||||
null, defaults to true.
|
||||
nullable: true
|
||||
type: boolean
|
||||
includedNamespaces:
|
||||
description: |-
|
||||
IncludedNamespaces is a slice of namespace names to include objects
|
||||
from. If empty, all namespaces are included.
|
||||
description: IncludedNamespaces is a slice of namespace names to include
|
||||
objects from. If empty, all namespaces are included.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedResources:
|
||||
description: |-
|
||||
IncludedResources is a slice of resource names to include
|
||||
description: IncludedResources is a slice of resource names to include
|
||||
in the restore. If empty, all resources in the backup are included.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
itemOperationTimeout:
|
||||
description: |-
|
||||
ItemOperationTimeout specifies the time used to wait for RestoreItemAction operations
|
||||
The default value is 4 hour.
|
||||
description: ItemOperationTimeout specifies the time used to wait
|
||||
for RestoreItemAction operations The default value is 1 hour.
|
||||
type: string
|
||||
labelSelector:
|
||||
description: |-
|
||||
LabelSelector is a metav1.LabelSelector to filter with
|
||||
when restoring individual objects from the backup. If empty
|
||||
or nil, all objects are included. Optional.
|
||||
description: LabelSelector is a metav1.LabelSelector to filter with
|
||||
when restoring individual objects from the backup. If empty or nil,
|
||||
all objects are included. Optional.
|
||||
nullable: true
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
description: A label selector requirement is a selector that
|
||||
contains values, a key, and an operator that relates the key
|
||||
and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
description: operator represents a key's relationship to
|
||||
a set of values. Valid operators are In, NotIn, Exists
|
||||
and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
description: values is an array of string values. If the
|
||||
operator is In or NotIn, the values array must be non-empty.
|
||||
If the operator is Exists or DoesNotExist, the values
|
||||
array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
@@ -299,58 +282,57 @@ spec:
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
description: matchLabels is a map of {key,value} pairs. A single
|
||||
{key,value} in the matchLabels map is equivalent to an element
|
||||
of matchExpressions, whose key field is "key", the operator
|
||||
is "In", and the values array contains only "value". The requirements
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
namespaceMapping:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
NamespaceMapping is a map of source namespace names
|
||||
to target namespace names to restore into. Any source
|
||||
namespaces not included in the map will be restored into
|
||||
namespaces of the same name.
|
||||
description: NamespaceMapping is a map of source namespace names to
|
||||
target namespace names to restore into. Any source namespaces not
|
||||
included in the map will be restored into namespaces of the same
|
||||
name.
|
||||
type: object
|
||||
orLabelSelectors:
|
||||
description: |-
|
||||
OrLabelSelectors is list of metav1.LabelSelector to filter with
|
||||
when restoring individual objects from the backup. If multiple provided
|
||||
they will be joined by the OR operator. LabelSelector as well as
|
||||
OrLabelSelectors cannot co-exist in restore request, only one of them
|
||||
can be used
|
||||
description: OrLabelSelectors is list of metav1.LabelSelector to filter
|
||||
with when restoring individual objects from the backup. If multiple
|
||||
provided they will be joined by the OR operator. LabelSelector as
|
||||
well as OrLabelSelectors cannot co-exist in restore request, only
|
||||
one of them can be used
|
||||
items:
|
||||
description: |-
|
||||
A label selector is a label query over a set of resources. The result of matchLabels and
|
||||
matchExpressions are ANDed. An empty label selector matches all objects. A null
|
||||
label selector matches no objects.
|
||||
description: A label selector is a label query over a set of resources.
|
||||
The result of matchLabels and matchExpressions are ANDed. An empty
|
||||
label selector matches all objects. A null label selector matches
|
||||
no objects.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
description: A label selector requirement is a selector that
|
||||
contains values, a key, and an operator that relates the
|
||||
key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
description: operator represents a key's relationship
|
||||
to a set of values. Valid operators are In, NotIn, Exists
|
||||
and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
description: values is an array of string values. If the
|
||||
operator is In or NotIn, the values array must be non-empty.
|
||||
If the operator is Exists or DoesNotExist, the values
|
||||
array must be empty. This array is replaced during a
|
||||
strategic merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -362,10 +344,11 @@ spec:
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
description: matchLabels is a map of {key,value} pairs. A single
|
||||
{key,value} in the matchLabels map is equivalent to an element
|
||||
of matchExpressions, whose key field is "key", the operator
|
||||
is "In", and the values array contains only "value". The requirements
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
@@ -382,10 +365,10 @@ spec:
|
||||
nullable: true
|
||||
properties:
|
||||
apiGroup:
|
||||
description: |-
|
||||
APIGroup is the group for the resource being referenced.
|
||||
If APIGroup is not specified, the specified Kind must be in the core API group.
|
||||
For any other third-party types, APIGroup is required.
|
||||
description: APIGroup is the group for the resource being referenced.
|
||||
If APIGroup is not specified, the specified Kind must be in
|
||||
the core API group. For any other third-party types, APIGroup
|
||||
is required.
|
||||
type: string
|
||||
kind:
|
||||
description: Kind is the type of resource being referenced
|
||||
@@ -399,15 +382,13 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
restorePVs:
|
||||
description: |-
|
||||
RestorePVs specifies whether to restore all included
|
||||
description: RestorePVs specifies whether to restore all included
|
||||
PVs from snapshot
|
||||
nullable: true
|
||||
type: boolean
|
||||
restoreStatus:
|
||||
description: |-
|
||||
RestoreStatus specifies which resources we should restore the status
|
||||
field. If nil, no objects are included. Optional.
|
||||
description: RestoreStatus specifies which resources we should restore
|
||||
the status field. If nil, no objects are included. Optional.
|
||||
nullable: true
|
||||
properties:
|
||||
excludedResources:
|
||||
@@ -418,71 +399,41 @@ spec:
|
||||
nullable: true
|
||||
type: array
|
||||
includedResources:
|
||||
description: |-
|
||||
IncludedResources specifies the resources to which will restore the status.
|
||||
If empty, it applies to all resources.
|
||||
description: IncludedResources specifies the resources to which
|
||||
will restore the status. If empty, it applies to all resources.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
type: object
|
||||
scheduleName:
|
||||
description: |-
|
||||
ScheduleName is the unique name of the Velero schedule to restore
|
||||
from. If specified, and BackupName is empty, Velero will restore
|
||||
from the most recent successful backup created from this schedule.
|
||||
description: ScheduleName is the unique name of the Velero schedule
|
||||
to restore from. If specified, and BackupName is empty, Velero will
|
||||
restore from the most recent successful backup created from this
|
||||
schedule.
|
||||
type: string
|
||||
uploaderConfig:
|
||||
description: UploaderConfig specifies the configuration for the restore.
|
||||
nullable: true
|
||||
properties:
|
||||
parallelFilesDownload:
|
||||
description: ParallelFilesDownload is the concurrency number setting
|
||||
for restore.
|
||||
type: integer
|
||||
writeSparseFiles:
|
||||
description: WriteSparseFiles is a flag to indicate whether write
|
||||
files sparsely or not.
|
||||
nullable: true
|
||||
type: boolean
|
||||
type: object
|
||||
required:
|
||||
- backupName
|
||||
type: object
|
||||
status:
|
||||
description: RestoreStatus captures the current status of a Velero restore
|
||||
properties:
|
||||
completionTimestamp:
|
||||
description: |-
|
||||
CompletionTimestamp records the time the restore operation was completed.
|
||||
Completion time is recorded even on failed restore.
|
||||
description: CompletionTimestamp records the time the restore operation
|
||||
was completed. Completion time is recorded even on failed restore.
|
||||
The server's time is used for StartTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
errors:
|
||||
description: |-
|
||||
Errors is a count of all error messages that were generated during
|
||||
execution of the restore. The actual errors are stored in object storage.
|
||||
description: Errors is a count of all error messages that were generated
|
||||
during execution of the restore. The actual errors are stored in
|
||||
object storage.
|
||||
type: integer
|
||||
failureReason:
|
||||
description: FailureReason is an error that caused the entire restore
|
||||
to fail.
|
||||
type: string
|
||||
hookStatus:
|
||||
description: HookStatus contains information about the status of the
|
||||
hooks.
|
||||
nullable: true
|
||||
properties:
|
||||
hooksAttempted:
|
||||
description: |-
|
||||
HooksAttempted is the total number of attempted hooks
|
||||
Specifically, HooksAttempted represents the number of hooks that failed to execute
|
||||
and the number of hooks that executed successfully.
|
||||
type: integer
|
||||
hooksFailed:
|
||||
description: HooksFailed is the total number of hooks which ended
|
||||
with an error
|
||||
type: integer
|
||||
type: object
|
||||
phase:
|
||||
description: Phase is the current state of the Restore
|
||||
enum:
|
||||
@@ -494,14 +445,11 @@ spec:
|
||||
- Completed
|
||||
- PartiallyFailed
|
||||
- Failed
|
||||
- Finalizing
|
||||
- FinalizingPartiallyFailed
|
||||
type: string
|
||||
progress:
|
||||
description: |-
|
||||
Progress contains information about the restore's execution progress. Note
|
||||
that this information is best-effort only -- if Velero fails to update it
|
||||
during a restore for any reason, it may be inaccurate/stale.
|
||||
description: Progress contains information about the restore's execution
|
||||
progress. Note that this information is best-effort only -- if Velero
|
||||
fails to update it during a restore for any reason, it may be inaccurate/stale.
|
||||
nullable: true
|
||||
properties:
|
||||
itemsRestored:
|
||||
@@ -509,46 +457,42 @@ spec:
|
||||
been restored so far
|
||||
type: integer
|
||||
totalItems:
|
||||
description: |-
|
||||
TotalItems is the total number of items to be restored. This number may change
|
||||
throughout the execution of the restore due to plugins that return additional related
|
||||
items to restore
|
||||
description: TotalItems is the total number of items to be restored.
|
||||
This number may change throughout the execution of the restore
|
||||
due to plugins that return additional related items to restore
|
||||
type: integer
|
||||
type: object
|
||||
restoreItemOperationsAttempted:
|
||||
description: |-
|
||||
RestoreItemOperationsAttempted is the total number of attempted
|
||||
async RestoreItemAction operations for this restore.
|
||||
description: RestoreItemOperationsAttempted is the total number of
|
||||
attempted async RestoreItemAction operations for this restore.
|
||||
type: integer
|
||||
restoreItemOperationsCompleted:
|
||||
description: |-
|
||||
RestoreItemOperationsCompleted is the total number of successfully completed
|
||||
async RestoreItemAction operations for this restore.
|
||||
description: RestoreItemOperationsCompleted is the total number of
|
||||
successfully completed async RestoreItemAction operations for this
|
||||
restore.
|
||||
type: integer
|
||||
restoreItemOperationsFailed:
|
||||
description: |-
|
||||
RestoreItemOperationsFailed is the total number of async
|
||||
RestoreItemAction operations for this restore which ended with an error.
|
||||
description: RestoreItemOperationsFailed is the total number of async
|
||||
RestoreItemAction operations for this restore which ended with an
|
||||
error.
|
||||
type: integer
|
||||
startTimestamp:
|
||||
description: |-
|
||||
StartTimestamp records the time the restore operation was started.
|
||||
The server's time is used for StartTimestamps
|
||||
description: StartTimestamp records the time the restore operation
|
||||
was started. The server's time is used for StartTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
validationErrors:
|
||||
description: |-
|
||||
ValidationErrors is a slice of all validation errors (if
|
||||
applicable)
|
||||
description: ValidationErrors is a slice of all validation errors
|
||||
(if applicable)
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
warnings:
|
||||
description: |-
|
||||
Warnings is a count of all warning messages that were generated during
|
||||
execution of the restore. The actual warnings are stored in object storage.
|
||||
description: Warnings is a count of all warning messages that were
|
||||
generated during execution of the restore. The actual warnings are
|
||||
stored in object storage.
|
||||
type: integer
|
||||
type: object
|
||||
type: object
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.12.0
|
||||
name: schedules.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
@@ -36,24 +36,18 @@ spec:
|
||||
name: v1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: |-
|
||||
Schedule is a Velero resource that represents a pre-scheduled or
|
||||
periodic Backup that should be run.
|
||||
description: Schedule is a Velero resource that represents a pre-scheduled
|
||||
or periodic Backup that should be run.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -64,80 +58,63 @@ spec:
|
||||
description: Paused specifies whether the schedule is paused or not
|
||||
type: boolean
|
||||
schedule:
|
||||
description: |-
|
||||
Schedule is a Cron expression defining when to run
|
||||
the Backup.
|
||||
description: Schedule is a Cron expression defining when to run the
|
||||
Backup.
|
||||
type: string
|
||||
skipImmediately:
|
||||
description: |-
|
||||
SkipImmediately specifies whether to skip backup if schedule is due immediately from `schedule.status.lastBackup` timestamp when schedule is unpaused or if schedule is new.
|
||||
If true, backup will be skipped immediately when schedule is unpaused if it is due based on .Status.LastBackupTimestamp or schedule is new, and will run at next schedule time.
|
||||
If false, backup will not be skipped immediately when schedule is unpaused, but will run at next schedule time.
|
||||
If empty, will follow server configuration (default: false).
|
||||
type: boolean
|
||||
template:
|
||||
description: |-
|
||||
Template is the definition of the Backup to be run
|
||||
on the provided schedule
|
||||
description: Template is the definition of the Backup to be run on
|
||||
the provided schedule
|
||||
properties:
|
||||
csiSnapshotTimeout:
|
||||
description: |-
|
||||
CSISnapshotTimeout specifies the time used to wait for CSI VolumeSnapshot status turns to
|
||||
ReadyToUse during creation, before returning error as timeout.
|
||||
The default value is 10 minute.
|
||||
description: CSISnapshotTimeout specifies the time used to wait
|
||||
for CSI VolumeSnapshot status turns to ReadyToUse during creation,
|
||||
before returning error as timeout. The default value is 10 minute.
|
||||
type: string
|
||||
datamover:
|
||||
description: |-
|
||||
DataMover specifies the data mover to be used by the backup.
|
||||
If DataMover is "" or "velero", the built-in data mover will be used.
|
||||
description: DataMover specifies the data mover to be used by
|
||||
the backup. If DataMover is "" or "velero", the built-in data
|
||||
mover will be used.
|
||||
type: string
|
||||
defaultVolumesToFsBackup:
|
||||
description: |-
|
||||
DefaultVolumesToFsBackup specifies whether pod volume file system backup should be used
|
||||
for all volumes by default.
|
||||
description: DefaultVolumesToFsBackup specifies whether pod volume
|
||||
file system backup should be used for all volumes by default.
|
||||
nullable: true
|
||||
type: boolean
|
||||
defaultVolumesToRestic:
|
||||
description: |-
|
||||
DefaultVolumesToRestic specifies whether restic should be used to take a
|
||||
backup of all pod volumes by default.
|
||||
|
||||
|
||||
Deprecated: this field is no longer used and will be removed entirely in future. Use DefaultVolumesToFsBackup instead.
|
||||
description: "DefaultVolumesToRestic specifies whether restic
|
||||
should be used to take a backup of all pod volumes by default.
|
||||
\n Deprecated: this field is no longer used and will be removed
|
||||
entirely in future. Use DefaultVolumesToFsBackup instead."
|
||||
nullable: true
|
||||
type: boolean
|
||||
excludedClusterScopedResources:
|
||||
description: |-
|
||||
ExcludedClusterScopedResources is a slice of cluster-scoped
|
||||
resource type names to exclude from the backup.
|
||||
If set to "*", all cluster-scoped resource types are excluded.
|
||||
The default value is empty.
|
||||
description: ExcludedClusterScopedResources is a slice of cluster-scoped
|
||||
resource type names to exclude from the backup. If set to "*",
|
||||
all cluster-scoped resource types are excluded. The default
|
||||
value is empty.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
excludedNamespaceScopedResources:
|
||||
description: |-
|
||||
ExcludedNamespaceScopedResources is a slice of namespace-scoped
|
||||
resource type names to exclude from the backup.
|
||||
If set to "*", all namespace-scoped resource types are excluded.
|
||||
The default value is empty.
|
||||
description: ExcludedNamespaceScopedResources is a slice of namespace-scoped
|
||||
resource type names to exclude from the backup. If set to "*",
|
||||
all namespace-scoped resource types are excluded. The default
|
||||
value is empty.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
excludedNamespaces:
|
||||
description: |-
|
||||
ExcludedNamespaces contains a list of namespaces that are not
|
||||
included in the backup.
|
||||
description: ExcludedNamespaces contains a list of namespaces
|
||||
that are not included in the backup.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
excludedResources:
|
||||
description: |-
|
||||
ExcludedResources is a slice of resource names that are not
|
||||
included in the backup.
|
||||
description: ExcludedResources is a slice of resource names that
|
||||
are not included in the backup.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
@@ -150,9 +127,9 @@ spec:
|
||||
description: Resources are hooks that should be executed when
|
||||
backing up individual instances of a resource.
|
||||
items:
|
||||
description: |-
|
||||
BackupResourceHookSpec defines one or more BackupResourceHooks that should be executed based on
|
||||
the rules defined for namespaces, resources, and label selector.
|
||||
description: BackupResourceHookSpec defines one or more
|
||||
BackupResourceHooks that should be executed based on the
|
||||
rules defined for namespaces, resources, and label selector.
|
||||
properties:
|
||||
excludedNamespaces:
|
||||
description: ExcludedNamespaces specifies the namespaces
|
||||
@@ -169,16 +146,16 @@ spec:
|
||||
nullable: true
|
||||
type: array
|
||||
includedNamespaces:
|
||||
description: |-
|
||||
IncludedNamespaces specifies the namespaces to which this hook spec applies. If empty, it applies
|
||||
description: IncludedNamespaces specifies the namespaces
|
||||
to which this hook spec applies. If empty, it applies
|
||||
to all namespaces.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedResources:
|
||||
description: |-
|
||||
IncludedResources specifies the resources to which this hook spec applies. If empty, it applies
|
||||
description: IncludedResources specifies the resources
|
||||
to which this hook spec applies. If empty, it applies
|
||||
to all resources.
|
||||
items:
|
||||
type: string
|
||||
@@ -193,25 +170,26 @@ spec:
|
||||
description: matchExpressions is a list of label
|
||||
selector requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
description: A label selector requirement is a
|
||||
selector that contains values, a key, and an
|
||||
operator that relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the
|
||||
selector applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
description: operator represents a key's relationship
|
||||
to a set of values. Valid operators are
|
||||
In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
description: values is an array of string
|
||||
values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the
|
||||
operator is Exists or DoesNotExist, the
|
||||
values array must be empty. This array is
|
||||
replaced during a strategic merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -223,10 +201,12 @@ spec:
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
description: matchLabels is a map of {key,value}
|
||||
pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions,
|
||||
whose key field is "key", the operator is "In",
|
||||
and the values array contains only "value". The
|
||||
requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
@@ -234,9 +214,10 @@ spec:
|
||||
description: Name is the name of this hook.
|
||||
type: string
|
||||
post:
|
||||
description: |-
|
||||
PostHooks is a list of BackupResourceHooks to execute after storing the item in the backup.
|
||||
These are executed after all "additional items" from item actions are processed.
|
||||
description: PostHooks is a list of BackupResourceHooks
|
||||
to execute after storing the item in the backup. These
|
||||
are executed after all "additional items" from item
|
||||
actions are processed.
|
||||
items:
|
||||
description: BackupResourceHook defines a hook for
|
||||
a resource.
|
||||
@@ -252,9 +233,10 @@ spec:
|
||||
minItems: 1
|
||||
type: array
|
||||
container:
|
||||
description: |-
|
||||
Container is the container in the pod where the command should be executed. If not specified,
|
||||
the pod's first container is used.
|
||||
description: Container is the container in
|
||||
the pod where the command should be executed.
|
||||
If not specified, the pod's first container
|
||||
is used.
|
||||
type: string
|
||||
onError:
|
||||
description: OnError specifies how Velero
|
||||
@@ -265,9 +247,10 @@ spec:
|
||||
- Fail
|
||||
type: string
|
||||
timeout:
|
||||
description: |-
|
||||
Timeout defines the maximum amount of time Velero should wait for the hook to complete before
|
||||
considering the execution a failure.
|
||||
description: Timeout defines the maximum amount
|
||||
of time Velero should wait for the hook
|
||||
to complete before considering the execution
|
||||
a failure.
|
||||
type: string
|
||||
required:
|
||||
- command
|
||||
@@ -277,9 +260,10 @@ spec:
|
||||
type: object
|
||||
type: array
|
||||
pre:
|
||||
description: |-
|
||||
PreHooks is a list of BackupResourceHooks to execute prior to storing the item in the backup.
|
||||
These are executed before any "additional items" from item actions are processed.
|
||||
description: PreHooks is a list of BackupResourceHooks
|
||||
to execute prior to storing the item in the backup.
|
||||
These are executed before any "additional items" from
|
||||
item actions are processed.
|
||||
items:
|
||||
description: BackupResourceHook defines a hook for
|
||||
a resource.
|
||||
@@ -295,9 +279,10 @@ spec:
|
||||
minItems: 1
|
||||
type: array
|
||||
container:
|
||||
description: |-
|
||||
Container is the container in the pod where the command should be executed. If not specified,
|
||||
the pod's first container is used.
|
||||
description: Container is the container in
|
||||
the pod where the command should be executed.
|
||||
If not specified, the pod's first container
|
||||
is used.
|
||||
type: string
|
||||
onError:
|
||||
description: OnError specifies how Velero
|
||||
@@ -308,9 +293,10 @@ spec:
|
||||
- Fail
|
||||
type: string
|
||||
timeout:
|
||||
description: |-
|
||||
Timeout defines the maximum amount of time Velero should wait for the hook to complete before
|
||||
considering the execution a failure.
|
||||
description: Timeout defines the maximum amount
|
||||
of time Velero should wait for the hook
|
||||
to complete before considering the execution
|
||||
a failure.
|
||||
type: string
|
||||
required:
|
||||
- command
|
||||
@@ -326,56 +312,50 @@ spec:
|
||||
type: array
|
||||
type: object
|
||||
includeClusterResources:
|
||||
description: |-
|
||||
IncludeClusterResources specifies whether cluster-scoped resources
|
||||
should be included for consideration in the backup.
|
||||
description: IncludeClusterResources specifies whether cluster-scoped
|
||||
resources should be included for consideration in the backup.
|
||||
nullable: true
|
||||
type: boolean
|
||||
includedClusterScopedResources:
|
||||
description: |-
|
||||
IncludedClusterScopedResources is a slice of cluster-scoped
|
||||
resource type names to include in the backup.
|
||||
If set to "*", all cluster-scoped resource types are included.
|
||||
The default value is empty, which means only related
|
||||
cluster-scoped resources are included.
|
||||
description: IncludedClusterScopedResources is a slice of cluster-scoped
|
||||
resource type names to include in the backup. If set to "*",
|
||||
all cluster-scoped resource types are included. The default
|
||||
value is empty, which means only related cluster-scoped resources
|
||||
are included.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedNamespaceScopedResources:
|
||||
description: |-
|
||||
IncludedNamespaceScopedResources is a slice of namespace-scoped
|
||||
resource type names to include in the backup.
|
||||
The default value is "*".
|
||||
description: IncludedNamespaceScopedResources is a slice of namespace-scoped
|
||||
resource type names to include in the backup. The default value
|
||||
is "*".
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedNamespaces:
|
||||
description: |-
|
||||
IncludedNamespaces is a slice of namespace names to include objects
|
||||
from. If empty, all namespaces are included.
|
||||
description: IncludedNamespaces is a slice of namespace names
|
||||
to include objects from. If empty, all namespaces are included.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedResources:
|
||||
description: |-
|
||||
IncludedResources is a slice of resource names to include
|
||||
in the backup. If empty, all resources are included.
|
||||
description: IncludedResources is a slice of resource names to
|
||||
include in the backup. If empty, all resources are included.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
itemOperationTimeout:
|
||||
description: |-
|
||||
ItemOperationTimeout specifies the time used to wait for asynchronous BackupItemAction operations
|
||||
The default value is 4 hour.
|
||||
description: ItemOperationTimeout specifies the time used to wait
|
||||
for asynchronous BackupItemAction operations The default value
|
||||
is 1 hour.
|
||||
type: string
|
||||
labelSelector:
|
||||
description: |-
|
||||
LabelSelector is a metav1.LabelSelector to filter with
|
||||
when adding individual objects to the backup. If empty
|
||||
description: LabelSelector is a metav1.LabelSelector to filter
|
||||
with when adding individual objects to the backup. If empty
|
||||
or nil, all objects are included. Optional.
|
||||
nullable: true
|
||||
properties:
|
||||
@@ -383,25 +363,25 @@ spec:
|
||||
description: matchExpressions is a list of label selector
|
||||
requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
description: A label selector requirement is a selector
|
||||
that contains values, a key, and an operator that relates
|
||||
the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector
|
||||
applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
description: operator represents a key's relationship
|
||||
to a set of values. Valid operators are In, NotIn,
|
||||
Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
description: values is an array of string values. If
|
||||
the operator is In or NotIn, the values array must
|
||||
be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced
|
||||
during a strategic merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -413,10 +393,11 @@ spec:
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
description: matchLabels is a map of {key,value} pairs. A
|
||||
single {key,value} in the matchLabels map is equivalent
|
||||
to an element of matchExpressions, whose key field is "key",
|
||||
the operator is "In", and the values array contains only
|
||||
"value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
@@ -428,41 +409,40 @@ spec:
|
||||
type: object
|
||||
type: object
|
||||
orLabelSelectors:
|
||||
description: |-
|
||||
OrLabelSelectors is list of metav1.LabelSelector to filter with
|
||||
when adding individual objects to the backup. If multiple provided
|
||||
they will be joined by the OR operator. LabelSelector as well as
|
||||
OrLabelSelectors cannot co-exist in backup request, only one of them
|
||||
can be used.
|
||||
description: OrLabelSelectors is list of metav1.LabelSelector
|
||||
to filter with when adding individual objects to the backup.
|
||||
If multiple provided they will be joined by the OR operator.
|
||||
LabelSelector as well as OrLabelSelectors cannot co-exist in
|
||||
backup request, only one of them can be used.
|
||||
items:
|
||||
description: |-
|
||||
A label selector is a label query over a set of resources. The result of matchLabels and
|
||||
matchExpressions are ANDed. An empty label selector matches all objects. A null
|
||||
label selector matches no objects.
|
||||
description: A label selector is a label query over a set of
|
||||
resources. The result of matchLabels and matchExpressions
|
||||
are ANDed. An empty label selector matches all objects. A
|
||||
null label selector matches no objects.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector
|
||||
requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
description: A label selector requirement is a selector
|
||||
that contains values, a key, and an operator that relates
|
||||
the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector
|
||||
applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
description: operator represents a key's relationship
|
||||
to a set of values. Valid operators are In, NotIn,
|
||||
Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
description: values is an array of string values.
|
||||
If the operator is In or NotIn, the values array
|
||||
must be non-empty. If the operator is Exists or
|
||||
DoesNotExist, the values array must be empty. This
|
||||
array is replaced during a strategic merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
@@ -474,10 +454,11 @@ spec:
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
description: matchLabels is a map of {key,value} pairs.
|
||||
A single {key,value} in the matchLabels map is equivalent
|
||||
to an element of matchExpressions, whose key field is
|
||||
"key", the operator is "In", and the values array contains
|
||||
only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
@@ -486,10 +467,11 @@ spec:
|
||||
orderedResources:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
OrderedResources specifies the backup order of resources of specific Kind.
|
||||
The map key is the resource name and value is a list of object names separated by commas.
|
||||
Each resource name has format "namespace/objectname". For cluster resources, simply use "objectname".
|
||||
description: OrderedResources specifies the backup order of resources
|
||||
of specific Kind. The map key is the resource name and value
|
||||
is a list of object names separated by commas. Each resource
|
||||
name has format "namespace/objectname". For cluster resources,
|
||||
simply use "objectname".
|
||||
nullable: true
|
||||
type: object
|
||||
resourcePolicy:
|
||||
@@ -497,10 +479,10 @@ spec:
|
||||
policies that backup should follow
|
||||
properties:
|
||||
apiGroup:
|
||||
description: |-
|
||||
APIGroup is the group for the resource being referenced.
|
||||
If APIGroup is not specified, the specified Kind must be in the core API group.
|
||||
For any other third-party types, APIGroup is required.
|
||||
description: APIGroup is the group for the resource being
|
||||
referenced. If APIGroup is not specified, the specified
|
||||
Kind must be in the core API group. For any other third-party
|
||||
types, APIGroup is required.
|
||||
type: string
|
||||
kind:
|
||||
description: Kind is the type of resource being referenced
|
||||
@@ -519,10 +501,9 @@ spec:
|
||||
nullable: true
|
||||
type: boolean
|
||||
snapshotVolumes:
|
||||
description: |-
|
||||
SnapshotVolumes specifies whether to take snapshots
|
||||
of any PV's referenced in the set of objects included
|
||||
in the Backup.
|
||||
description: SnapshotVolumes specifies whether to take snapshots
|
||||
of any PV's referenced in the set of objects included in the
|
||||
Backup.
|
||||
nullable: true
|
||||
type: boolean
|
||||
storageLocation:
|
||||
@@ -530,20 +511,9 @@ spec:
|
||||
a BackupStorageLocation where the backup should be stored.
|
||||
type: string
|
||||
ttl:
|
||||
description: |-
|
||||
TTL is a time.Duration-parseable string describing how long
|
||||
the Backup should be retained for.
|
||||
description: TTL is a time.Duration-parseable string describing
|
||||
how long the Backup should be retained for.
|
||||
type: string
|
||||
uploaderConfig:
|
||||
description: UploaderConfig specifies the configuration for the
|
||||
uploader.
|
||||
nullable: true
|
||||
properties:
|
||||
parallelFilesUpload:
|
||||
description: ParallelFilesUpload is the number of files parallel
|
||||
uploads to perform when using the uploader.
|
||||
type: integer
|
||||
type: object
|
||||
volumeSnapshotLocations:
|
||||
description: VolumeSnapshotLocations is a list containing names
|
||||
of VolumeSnapshotLocations associated with this backup.
|
||||
@@ -552,9 +522,8 @@ spec:
|
||||
type: array
|
||||
type: object
|
||||
useOwnerReferencesInBackup:
|
||||
description: |-
|
||||
UseOwnerReferencesBackup specifies whether to use
|
||||
OwnerReferences on backups created by this Schedule.
|
||||
description: UseOwnerReferencesBackup specifies whether to use OwnerReferences
|
||||
on backups created by this Schedule.
|
||||
nullable: true
|
||||
type: boolean
|
||||
required:
|
||||
@@ -565,17 +534,11 @@ spec:
|
||||
description: ScheduleStatus captures the current state of a Velero schedule
|
||||
properties:
|
||||
lastBackup:
|
||||
description: |-
|
||||
LastBackup is the last time a Backup was run for this
|
||||
description: LastBackup is the last time a Backup was run for this
|
||||
Schedule schedule
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
lastSkipped:
|
||||
description: LastSkipped is the last time a Schedule was skipped
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
phase:
|
||||
description: Phase is the current phase of the Schedule
|
||||
enum:
|
||||
@@ -584,9 +547,8 @@ spec:
|
||||
- FailedValidation
|
||||
type: string
|
||||
validationErrors:
|
||||
description: |-
|
||||
ValidationErrors is a slice of all validation errors (if
|
||||
applicable)
|
||||
description: ValidationErrors is a slice of all validation errors
|
||||
(if applicable)
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.12.0
|
||||
name: serverstatusrequests.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
@@ -19,24 +19,18 @@ spec:
|
||||
- name: v1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: |-
|
||||
ServerStatusRequest is a request to access current status information about
|
||||
the Velero server.
|
||||
description: ServerStatusRequest is a request to access current status information
|
||||
about the Velero server.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -69,9 +63,8 @@ spec:
|
||||
nullable: true
|
||||
type: array
|
||||
processedTimestamp:
|
||||
description: |-
|
||||
ProcessedTimestamp is when the ServerStatusRequest was processed
|
||||
by the ServerStatusRequestController.
|
||||
description: ProcessedTimestamp is when the ServerStatusRequest was
|
||||
processed by the ServerStatusRequestController.
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.12.0
|
||||
name: volumesnapshotlocations.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
@@ -23,19 +23,14 @@ spec:
|
||||
snapshots.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -57,10 +52,8 @@ spec:
|
||||
valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
Name of the referent.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||
type: string
|
||||
optional:
|
||||
description: Specify whether the Secret or its key must be defined
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.12.0
|
||||
name: datadownloads.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
@@ -52,19 +52,14 @@ spec:
|
||||
and data mover controller for the datamover restore operation
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -72,14 +67,12 @@ spec:
|
||||
description: DataDownloadSpec is the specification for a DataDownload.
|
||||
properties:
|
||||
backupStorageLocation:
|
||||
description: |-
|
||||
BackupStorageLocation is the name of the backup storage location
|
||||
where the backup repository is stored.
|
||||
description: BackupStorageLocation is the name of the backup storage
|
||||
location where the backup repository is stored.
|
||||
type: string
|
||||
cancel:
|
||||
description: |-
|
||||
Cancel indicates request to cancel the ongoing DataDownload. It can be set
|
||||
when the DataDownload is in InProgress phase
|
||||
description: Cancel indicates request to cancel the ongoing DataDownload.
|
||||
It can be set when the DataDownload is in InProgress phase
|
||||
type: boolean
|
||||
dataMoverConfig:
|
||||
additionalProperties:
|
||||
@@ -88,23 +81,22 @@ spec:
|
||||
fields.
|
||||
type: object
|
||||
datamover:
|
||||
description: |-
|
||||
DataMover specifies the data mover to be used by the backup.
|
||||
If DataMover is "" or "velero", the built-in data mover will be used.
|
||||
description: DataMover specifies the data mover to be used by the
|
||||
backup. If DataMover is "" or "velero", the built-in data mover
|
||||
will be used.
|
||||
type: string
|
||||
operationTimeout:
|
||||
description: |-
|
||||
OperationTimeout specifies the time used to wait internal operations,
|
||||
before returning error as timeout.
|
||||
description: OperationTimeout specifies the time used to wait internal
|
||||
operations, before returning error as timeout.
|
||||
type: string
|
||||
snapshotID:
|
||||
description: SnapshotID is the ID of the Velero backup snapshot to
|
||||
be restored from.
|
||||
type: string
|
||||
sourceNamespace:
|
||||
description: |-
|
||||
SourceNamespace is the original namespace where the volume is backed up from.
|
||||
It may be different from SourcePVC's namespace if namespace is remapped during restore.
|
||||
description: SourceNamespace is the original namespace where the volume
|
||||
is backed up from. It may be different from SourcePVC's namespace
|
||||
if namespace is remapped during restore.
|
||||
type: string
|
||||
targetVolume:
|
||||
description: TargetVolume is the information of the target PVC and
|
||||
@@ -137,10 +129,9 @@ spec:
|
||||
description: DataDownloadStatus is the current status of a DataDownload.
|
||||
properties:
|
||||
completionTimestamp:
|
||||
description: |-
|
||||
CompletionTimestamp records the time a restore was completed.
|
||||
Completion time is recorded even on failed restores.
|
||||
The server's time is used for CompletionTimestamps
|
||||
description: CompletionTimestamp records the time a restore was completed.
|
||||
Completion time is recorded even on failed restores. The server's
|
||||
time is used for CompletionTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
@@ -163,10 +154,9 @@ spec:
|
||||
- Failed
|
||||
type: string
|
||||
progress:
|
||||
description: |-
|
||||
Progress holds the total number of bytes of the snapshot and the current
|
||||
number of restored bytes. This can be used to display progress information
|
||||
about the restore operation.
|
||||
description: Progress holds the total number of bytes of the snapshot
|
||||
and the current number of restored bytes. This can be used to display
|
||||
progress information about the restore operation.
|
||||
properties:
|
||||
bytesDone:
|
||||
format: int64
|
||||
@@ -176,8 +166,7 @@ spec:
|
||||
type: integer
|
||||
type: object
|
||||
startTimestamp:
|
||||
description: |-
|
||||
StartTimestamp records the time a restore was started.
|
||||
description: StartTimestamp records the time a restore was started.
|
||||
The server's time is used for StartTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.12.0
|
||||
name: datauploads.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
@@ -53,19 +53,14 @@ spec:
|
||||
data mover controller for the datamover backup operation
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
@@ -73,14 +68,12 @@ spec:
|
||||
description: DataUploadSpec is the specification for a DataUpload.
|
||||
properties:
|
||||
backupStorageLocation:
|
||||
description: |-
|
||||
BackupStorageLocation is the name of the backup storage location
|
||||
where the backup repository is stored.
|
||||
description: BackupStorageLocation is the name of the backup storage
|
||||
location where the backup repository is stored.
|
||||
type: string
|
||||
cancel:
|
||||
description: |-
|
||||
Cancel indicates request to cancel the ongoing DataUpload. It can be set
|
||||
when the DataUpload is in InProgress phase
|
||||
description: Cancel indicates request to cancel the ongoing DataUpload.
|
||||
It can be set when the DataUpload is in InProgress phase
|
||||
type: boolean
|
||||
csiSnapshot:
|
||||
description: If SnapshotType is CSI, CSISnapshot provides the information
|
||||
@@ -111,23 +104,22 @@ spec:
|
||||
nullable: true
|
||||
type: object
|
||||
datamover:
|
||||
description: |-
|
||||
DataMover specifies the data mover to be used by the backup.
|
||||
If DataMover is "" or "velero", the built-in data mover will be used.
|
||||
description: DataMover specifies the data mover to be used by the
|
||||
backup. If DataMover is "" or "velero", the built-in data mover
|
||||
will be used.
|
||||
type: string
|
||||
operationTimeout:
|
||||
description: |-
|
||||
OperationTimeout specifies the time used to wait internal operations,
|
||||
before returning error as timeout.
|
||||
description: OperationTimeout specifies the time used to wait internal
|
||||
operations, before returning error as timeout.
|
||||
type: string
|
||||
snapshotType:
|
||||
description: SnapshotType is the type of the snapshot to be backed
|
||||
up.
|
||||
type: string
|
||||
sourceNamespace:
|
||||
description: |-
|
||||
SourceNamespace is the original namespace where the volume is backed up from.
|
||||
It is the same namespace for SourcePVC and CSI namespaced objects.
|
||||
description: SourceNamespace is the original namespace where the volume
|
||||
is backed up from. It is the same namespace for SourcePVC and CSI
|
||||
namespaced objects.
|
||||
type: string
|
||||
sourcePVC:
|
||||
description: SourcePVC is the name of the PVC which the snapshot is
|
||||
@@ -144,11 +136,10 @@ spec:
|
||||
description: DataUploadStatus is the current status of a DataUpload.
|
||||
properties:
|
||||
completionTimestamp:
|
||||
description: |-
|
||||
CompletionTimestamp records the time a backup was completed.
|
||||
Completion time is recorded even on failed backups.
|
||||
Completion time is recorded before uploading the backup object.
|
||||
The server's time is used for CompletionTimestamps
|
||||
description: CompletionTimestamp records the time a backup was completed.
|
||||
Completion time is recorded even on failed backups. Completion time
|
||||
is recorded before uploading the backup object. The server's time
|
||||
is used for CompletionTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
@@ -182,10 +173,9 @@ spec:
|
||||
- Failed
|
||||
type: string
|
||||
progress:
|
||||
description: |-
|
||||
Progress holds the total number of bytes of the volume and the current
|
||||
number of backed up bytes. This can be used to display progress information
|
||||
about the backup operation.
|
||||
description: Progress holds the total number of bytes of the volume
|
||||
and the current number of backed up bytes. This can be used to display
|
||||
progress information about the backup operation.
|
||||
properties:
|
||||
bytesDone:
|
||||
format: int64
|
||||
@@ -199,10 +189,8 @@ spec:
|
||||
backup repository.
|
||||
type: string
|
||||
startTimestamp:
|
||||
description: |-
|
||||
StartTimestamp records the time a backup was started.
|
||||
Separate from CreationTimestamp, since that value changes
|
||||
on restores.
|
||||
description: StartTimestamp records the time a backup was started.
|
||||
Separate from CreationTimestamp, since that value changes on restores.
|
||||
The server's time is used for StartTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,344 +0,0 @@
|
||||
# Extend VolumePolicies to support more actions
|
||||
|
||||
## Abstract
|
||||
|
||||
Currently, the [VolumePolicies feature](https://github.com/vmware-tanzu/velero/blob/main/design/Implemented/handle-backup-of-volumes-by-resources-filters.md) which can be used to filter/handle volumes during backup only supports the skip action on matching conditions. Users need more actions to be supported.
|
||||
|
||||
## Background
|
||||
|
||||
The `VolumePolicies` feature was introduced in Velero 1.11 as a flexible way to handle volumes. The main agenda of
|
||||
introducing the VolumePolicies feature was to improve the overall user experience when performing backup operations
|
||||
for volume resources, the feature enables users to group volumes according the `conditions` (criteria) specified and
|
||||
also lets you specify the `action` that velero needs to take for these grouped volumes during the backup operation.
|
||||
The limitation being that currently `VolumePolicies` only supports `skip` as an action, We want to extend the `action`
|
||||
functionality to support more usable options like `fs-backup` (File system backup) and `snapshot` (VolumeSnapshots).
|
||||
|
||||
## Goals
|
||||
- Extending the VolumePolicies to support more actions like `fs-backup` (File system backup) and `snapshot` (VolumeSnapshots).
|
||||
- Improve user experience when backing up Volumes via Velero
|
||||
|
||||
## Non-Goals
|
||||
- No changes to existing approaches to opt-in/opt-out annotations for volumes
|
||||
- No changes to existing `VolumePolicies` functionalities
|
||||
- No additions or implementations to support more granular actions like `snapshot-csi` and `snapshot-datamover`. These actions can be implemented as a future enhancement
|
||||
|
||||
|
||||
## Use-cases/Scenarios
|
||||
|
||||
**Use-case 1:**
|
||||
- A user wants to use `snapshot` (volumesnapshots) backup option for all the csi supported volumes and `fs-backup` for the rest of the volumes.
|
||||
- Currently, velero supports this use-case but the user experience is not that great.
|
||||
- The user will have to individually annotate the volume mounting pod with the annotation "backup.velero.io/backup-volumes" for `fs-backup`
|
||||
- This becomes cumbersome at scale.
|
||||
- Using `VolumePolicies`, the user can just specify 2 simple `VolumePolicies` like for csi supported volumes as `snapshot` action and rest can be backed up`fs-backup` action:
|
||||
```yaml
|
||||
version: v1
|
||||
volumePolicies:
|
||||
- conditions:
|
||||
storageClass:
|
||||
- gp2
|
||||
action:
|
||||
type: snapshot
|
||||
- conditions: {}
|
||||
action:
|
||||
type: fs-backup
|
||||
```
|
||||
|
||||
**Use-case 2:**
|
||||
- A user wants to use `fs-backup` for nfs volumes pertaining to a particular server
|
||||
- In such a scenario the user can just specify a `VolumePolicy` like:
|
||||
```yaml
|
||||
version: v1
|
||||
volumePolicies:
|
||||
- conditions:
|
||||
nfs:
|
||||
server: 192.168.200.90
|
||||
action:
|
||||
type: fs-backup
|
||||
```
|
||||
## High-Level Design
|
||||
- When the VolumePolicy action is set as `fs-backup` the backup workflow modifications would be:
|
||||
- We call [backupItem() -> backupItemInternal()](https://github.com/vmware-tanzu/velero/blob/main/pkg/backup/item_backupper.go#L95) on all the items that are to be backed up
|
||||
- Here when we encounter [Pod as an item ](https://github.com/vmware-tanzu/velero/blob/main/pkg/backup/item_backupper.go#L195)
|
||||
- We will have to modify the backup workflow to account for the `fs-backup` VolumePolicy action
|
||||
|
||||
|
||||
- When the VolumePolicy action is set as `snapshot` the backup workflow modifications would be:
|
||||
- Once again, We call [backupItem() -> backupItemInternal()](https://github.com/vmware-tanzu/velero/blob/main/pkg/backup/item_backupper.go#L95) on all the items that are to be backed up
|
||||
- Here when we encounter [Persistent Volume as an item](https://github.com/vmware-tanzu/velero/blob/d4128542590470b204a642ee43311921c11db880/pkg/backup/item_backupper.go#L253)
|
||||
- And we call the [takePVSnapshot func](https://github.com/vmware-tanzu/velero/blob/d4128542590470b204a642ee43311921c11db880/pkg/backup/item_backupper.go#L508)
|
||||
- We need to modify the takePVSnapshot function to account for the `snapshot` VolumePolicy action.
|
||||
- In case of csi snapshots for PVC objects, these snapshot actions are taken by the velero-plugin-for-csi, we need to modify the [executeActions()](https://github.com/vmware-tanzu/velero/blob/512fe0dabdcb3bbf1ca68a9089056ae549663bcf/pkg/backup/item_backupper.go#L232) function to account for the `snapshot` VolumePolicy action.
|
||||
|
||||
**Note:** `Snapshot` action can either be a native snapshot or a csi snapshot, as is the case with the current flow where velero itself makes the decision based on the backup CR.
|
||||
|
||||
## Detailed Design
|
||||
- Update VolumePolicy action type validation to account for `fs-backup` and `snapshot` as valid VolumePolicy actions.
|
||||
- Modifications needed for `fs-backup` action:
|
||||
- Now based on the specification of volume policy on backup request we will decide whether to go via legacy pod annotations approach or the newer volume policy based fs-backup action approach.
|
||||
- If there is a presence of volume policy(fs-backup/snapshot) on the backup request that matches as an action for a volume we use the newer volume policy approach to get the list of the volumes for `fs-backup` action
|
||||
- Else continue with the annotation based legacy approach workflow.
|
||||
|
||||
- Modifications needed for `snapshot` action:
|
||||
- In the [takePVSnapshot function](https://github.com/vmware-tanzu/velero/blob/d4128542590470b204a642ee43311921c11db880/pkg/backup/item_backupper.go#L508) we will check the PV fits the volume policy criteria and see if the associated action is `snapshot`
|
||||
- If it is not snapshot then we skip the further workflow and avoid taking the snapshot of the PV
|
||||
- Similarly, For csi snapshot of PVC object, we need to do similar changes in [executeAction() function](https://github.com/vmware-tanzu/velero/blob/512fe0dabdcb3bbf1ca68a9089056ae549663bcf/pkg/backup/item_backupper.go#L348). we will check the PVC fits the volume policy criteria and see if the associated action is `snapshot` via csi plugin
|
||||
- If it is not snapshot then we skip the csi BIA execute action and avoid taking the snapshot of the PVC by not invoking the csi plugin action for the PVC
|
||||
|
||||
**Note:**
|
||||
- When we are using the `VolumePolicy` approach for backing up the volumes then the volume policy criteria and action need to be specific and explicit, there is no default behavior, if a volume matches `fs-backup` action then `fs-backup` method will be used for that volume and similarly if the volume matches the criteria for `snapshot` action then the snapshot workflow will be used for the volume backup.
|
||||
- Another thing to note is the workflow proposed in this design uses the legacy `opt-in/opt-out` approach as a fallback option. For instance, the user specifies a VolumePolicy but for a particular volume included in the backup there are no actions(fs-backup/snapshot) matching in the volume policy for that volume, in such a scenario the legacy approach will be used for backing up the particular volume.
|
||||
- The relation between the `VolumePolicy` and the backup's legacy parameter `SnapshotVolumes`:
|
||||
- The `VolumePolicy`'s `snapshot` action matching for volume has higher priority. When there is a `snapshot` action matching for the selected volume, it will be backed by the snapshot way, no matter of the `backup.Spec.SnapshotVolumes` setting.
|
||||
- If there is no `snapshot` action matching the selected volume in the `VolumePolicy`, then the volume will be backed up by `snapshot` way, if the `backup.Spec.SnapshotVolumes` is not set to false.
|
||||
- The relation between the `VolumePolicy` and the backup's legacy filesystem `opt-in/opt-out` approach:
|
||||
- The `VolumePolicy`'s `fs-backup` action matching for volume has higher priority. When there is a `fs-backup` action matching for the selected volume, it will be backed by the fs-backup way, no matter of the `backup.Spec.DefaultVolumesToFsBackup` setting and the pod's `opt-in/opt-out` annotation setting.
|
||||
- If there is no `fs-backup` action matching the selected volume in the `VolumePolicy`, then the volume will be backed up by the legacy `opt-in/opt-out` way.
|
||||
|
||||
## Implementation
|
||||
|
||||
- The implementation should be included in velero 1.14
|
||||
|
||||
- We will introduce a `VolumeHelper` interface. It will consist of two methods:
|
||||
```go
|
||||
type VolumeHelper interface {
|
||||
ShouldPerformSnapshot(obj runtime.Unstructured, groupResource schema.GroupResource) (bool, error)
|
||||
ShouldPerformFSBackup(volume corev1api.Volume, pod corev1api.Pod) (bool, error)
|
||||
}
|
||||
```
|
||||
- The `VolumeHelperImpl` struct will implement the `VolumeHelper` interface and will consist of the functions that we will use through the backup workflow to accommodate volume policies for PVs and PVCs.
|
||||
```go
|
||||
type volumeHelperImpl struct {
|
||||
volumePolicy *resourcepolicies.Policies
|
||||
snapshotVolumes *bool
|
||||
logger logrus.FieldLogger
|
||||
client crclient.Client
|
||||
defaultVolumesToFSBackup bool
|
||||
backupExcludePVC bool
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
- We will create an instance of the structure `volumeHelperImpl` in `item_backupper.go`
|
||||
```go
|
||||
itemBackupper := &itemBackupper{
|
||||
...
|
||||
volumeHelperImpl: volumehelper.NewVolumeHelperImpl(
|
||||
resourcePolicy,
|
||||
backupRequest.Spec.SnapshotVolumes,
|
||||
log,
|
||||
kb.kbClient,
|
||||
boolptr.IsSetToTrue(backupRequest.Spec.DefaultVolumesToFsBackup),
|
||||
!backupRequest.ResourceIncludesExcludes.ShouldInclude(kuberesource.PersistentVolumeClaims.String()),
|
||||
),
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### FS-Backup
|
||||
- Regarding `fs-backup` action to decide whether to use legacy annotation based approach or volume policy based approach:
|
||||
- We will use the `vh.ShouldPerformFSBackup()` function from the `volumehelper` package
|
||||
- Functions involved in processing `fs-backup` volume policy action will somewhat look like:
|
||||
|
||||
```go
|
||||
func (v volumeHelperImpl) ShouldPerformFSBackup(volume corev1api.Volume, pod corev1api.Pod) (bool, error) {
|
||||
if !v.shouldIncludeVolumeInBackup(volume) {
|
||||
v.logger.Debugf("skip fs-backup action for pod %s's volume %s, due to not pass volume check.", pod.Namespace+"/"+pod.Name, volume.Name)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if v.volumePolicy != nil {
|
||||
pvc, err := kubeutil.GetPVCForPodVolume(&volume, &pod, v.client)
|
||||
if err != nil {
|
||||
v.logger.WithError(err).Errorf("fail to get PVC for pod %s", pod.Namespace+"/"+pod.Name)
|
||||
return false, err
|
||||
}
|
||||
pv, err := kubeutil.GetPVForPVC(pvc, v.client)
|
||||
if err != nil {
|
||||
v.logger.WithError(err).Errorf("fail to get PV for PVC %s", pvc.Namespace+"/"+pvc.Name)
|
||||
return false, err
|
||||
}
|
||||
|
||||
action, err := v.volumePolicy.GetMatchAction(pv)
|
||||
if err != nil {
|
||||
v.logger.WithError(err).Errorf("fail to get VolumePolicy match action for PV %s", pv.Name)
|
||||
return false, err
|
||||
}
|
||||
|
||||
if action != nil {
|
||||
if action.Type == resourcepolicies.FSBackup {
|
||||
v.logger.Infof("Perform fs-backup action for volume %s of pod %s due to volume policy match",
|
||||
volume.Name, pod.Namespace+"/"+pod.Name)
|
||||
return true, nil
|
||||
} else {
|
||||
v.logger.Infof("Skip fs-backup action for volume %s for pod %s because the action type is %s",
|
||||
volume.Name, pod.Namespace+"/"+pod.Name, action.Type)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if v.shouldPerformFSBackupLegacy(volume, pod) {
|
||||
v.logger.Infof("Perform fs-backup action for volume %s of pod %s due to opt-in/out way",
|
||||
volume.Name, pod.Namespace+"/"+pod.Name)
|
||||
return true, nil
|
||||
} else {
|
||||
v.logger.Infof("Skip fs-backup action for volume %s of pod %s due to opt-in/out way",
|
||||
volume.Name, pod.Namespace+"/"+pod.Name)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- The main function from the above will be called when we encounter Pods during the backup workflow:
|
||||
```go
|
||||
for _, volume := range pod.Spec.Volumes {
|
||||
shouldDoFSBackup, err := ib.volumeHelperImpl.ShouldPerformFSBackup(volume, *pod)
|
||||
if err != nil {
|
||||
backupErrs = append(backupErrs, errors.WithStack(err))
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
#### Snapshot (PV)
|
||||
|
||||
- Making sure that `snapshot` action is skipped for PVs that do not fit the volume policy criteria, for this we will use the `vh.ShouldPerformSnapshot` from the `VolumeHelperImpl(vh)` receiver.
|
||||
```go
|
||||
func (v *volumeHelperImpl) ShouldPerformSnapshot(obj runtime.Unstructured, groupResource schema.GroupResource) (bool, error) {
|
||||
// check if volume policy exists and also check if the object(pv/pvc) fits a volume policy criteria and see if the associated action is snapshot
|
||||
// if it is not snapshot then skip the code path for snapshotting the PV/PVC
|
||||
pvc := new(corev1api.PersistentVolumeClaim)
|
||||
pv := new(corev1api.PersistentVolume)
|
||||
var err error
|
||||
|
||||
if groupResource == kuberesource.PersistentVolumeClaims {
|
||||
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &pvc); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
pv, err = kubeutil.GetPVForPVC(pvc, v.client)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if groupResource == kuberesource.PersistentVolumes {
|
||||
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &pv); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if v.volumePolicy != nil {
|
||||
action, err := v.volumePolicy.GetMatchAction(pv)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// If there is a match action, and the action type is snapshot, return true,
|
||||
// or the action type is not snapshot, then return false.
|
||||
// If there is no match action, go on to the next check.
|
||||
if action != nil {
|
||||
if action.Type == resourcepolicies.Snapshot {
|
||||
v.logger.Infof(fmt.Sprintf("performing snapshot action for pv %s", pv.Name))
|
||||
return true, nil
|
||||
} else {
|
||||
v.logger.Infof("Skip snapshot action for pv %s as the action type is %s", pv.Name, action.Type)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If this PV is claimed, see if we've already taken a (pod volume backup)
|
||||
// snapshot of the contents of this PV. If so, don't take a snapshot.
|
||||
if pv.Spec.ClaimRef != nil {
|
||||
pods, err := podvolumeutil.GetPodsUsingPVC(
|
||||
pv.Spec.ClaimRef.Namespace,
|
||||
pv.Spec.ClaimRef.Name,
|
||||
v.client,
|
||||
)
|
||||
if err != nil {
|
||||
v.logger.WithError(err).Errorf("fail to get pod for PV %s", pv.Name)
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, pod := range pods {
|
||||
for _, vol := range pod.Spec.Volumes {
|
||||
if vol.PersistentVolumeClaim != nil &&
|
||||
vol.PersistentVolumeClaim.ClaimName == pv.Spec.ClaimRef.Name &&
|
||||
v.shouldPerformFSBackupLegacy(vol, pod) {
|
||||
v.logger.Infof("Skipping snapshot of pv %s because it is backed up with PodVolumeBackup.", pv.Name)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !boolptr.IsSetToFalse(v.snapshotVolumes) {
|
||||
// If the backup.Spec.SnapshotVolumes is not set, or set to true, then should take the snapshot.
|
||||
v.logger.Infof("performing snapshot action for pv %s as the snapshotVolumes is not set to false")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
v.logger.Infof(fmt.Sprintf("skipping snapshot action for pv %s possibly due to no volume policy setting or snapshotVolumes is false", pv.Name))
|
||||
return false, nil
|
||||
}
|
||||
```
|
||||
|
||||
- The function `ShouldPerformSnapshot` will be used as follows in `takePVSnapshot` function of the backup workflow:
|
||||
```go
|
||||
snapshotVolume, err := ib.volumeHelperImpl.ShouldPerformSnapshot(obj, kuberesource.PersistentVolumes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !snapshotVolume {
|
||||
log.Info(fmt.Sprintf("skipping volume snapshot for PV %s as it does not fit the volume policy criteria specified by the user for snapshot action", pv.Name))
|
||||
ib.trackSkippedPV(obj, kuberesource.PersistentVolumes, volumeSnapshotApproach, "does not satisfy the criteria for volume policy based snapshot action", log)
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
#### Snapshot (PVC)
|
||||
|
||||
- Making sure that `snapshot` action is skipped for PVCs that do not fit the volume policy criteria, for this we will again use the `vh.ShouldPerformSnapshot` from the `VolumeHelperImpl(vh)` receiver.
|
||||
- We will pass the `VolumeHelperImpl(vh)` instance in `executeActions` method so that it is available to use.
|
||||
```go
|
||||
|
||||
```
|
||||
- The above function will be used as follows in the `executeActions` function of backup workflow.
|
||||
- Considering the vSphere plugin doesn't support the VolumePolicy yet, don't use the VolumePolicy for vSphere plugin by now.
|
||||
```go
|
||||
if groupResource == kuberesource.PersistentVolumeClaims {
|
||||
if actionName == csiBIAPluginName {
|
||||
snapshotVolume, err := ib.volumeHelperImpl.ShouldPerformSnapshot(obj, kuberesource.PersistentVolumeClaims)
|
||||
if err != nil {
|
||||
return nil, itemFiles, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if !snapshotVolume {
|
||||
log.Info(fmt.Sprintf("skipping csi volume snapshot for PVC %s as it does not fit the volume policy criteria specified by the user for snapshot action", namespace+"/"+name))
|
||||
ib.trackSkippedPV(obj, kuberesource.PersistentVolumeClaims, volumeSnapshotApproach, "does not satisfy the criteria for volume policy based snapshot action", log)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Future Implementation
|
||||
It makes sense to add more specific actions in the future, once we deprecate the legacy opt-in/opt-out approach to keep things simple. Another point of note is, csi related action can be
|
||||
easier to implement once we decide to merge the csi plugin into main velero code flow.
|
||||
In the future, we envision the following actions that can be implemented:
|
||||
- `snapshot-native`: only use volume snapshotter (native cloud provider snapshots), do nothing if not present/not compatible
|
||||
- `snapshot-csi`: only use csi-plugin, don't use volume snapshotter(native cloud provider snapshots), don't use datamover even if snapshotMoveData is true
|
||||
- `snapshot-datamover`: only use csi with datamover, don't use volume snapshotter (native cloud provider snapshots), use datamover even if snapshotMoveData is false
|
||||
|
||||
**Note:** The above actions are just suggestions for future scope, we may not use/implement them as is. We could definitely merge these suggested actions as `Snapshot` actions and use volume policy parameters and criteria to segregate them instead of making the user explicitly supply the action names to such granular levels.
|
||||
|
||||
## Related to Design
|
||||
|
||||
[Handle backup of volumes by resources filters](https://github.com/vmware-tanzu/velero/blob/main/design/Implemented/handle-backup-of-volumes-by-resources-filters.md)
|
||||
|
||||
## Alternatives Considered
|
||||
Same as the earlier design as this is an extension of the original VolumePolicies design
|
||||
@@ -65,7 +65,7 @@ This page contains a pre-migration checklist for ensuring a repo migration goes
|
||||
|
||||
#### Updating Netlify
|
||||
|
||||
The settings for Netlify should remain the same, except that it now needs to be installed in the new repo. The instructions on how to install Netlify on the new repo are here: https://www.netlify.com/docs/github-permissions/.
|
||||
The settings for Netflify should remain the same, except that it now needs to be installed in the new repo. The instructions on how to install Netlify on the new repo are here: https://www.netlify.com/docs/github-permissions/.
|
||||
|
||||
#### Communication strategy
|
||||
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
# Node-agent Load Affinity Design
|
||||
|
||||
## Glossary & Abbreviation
|
||||
|
||||
**Velero Generic Data Path (VGDP)**: VGDP is the collective modules that is introduced in [Unified Repository design][1]. Velero uses these modules to finish data transfer for various purposes (i.e., PodVolume backup/restore, Volume Snapshot Data Movement). VGDP modules include uploaders and the backup repository.
|
||||
|
||||
**Exposer**: Exposer is a module that is introduced in [Volume Snapshot Data Movement Design][2]. Velero uses this module to expose the volume snapshots to Velero node-agent pods or node-agent associated pods so as to complete the data movement from the snapshots.
|
||||
|
||||
## Background
|
||||
|
||||
Velero node-agent is a daemonset hosting controllers and VGDP modules to complete the concrete work of backups/restores, i.e., PodVolume backup/restore, Volume Snapshot Data Movement backup/restore.
|
||||
Specifically, node-agent runs DataUpload controllers to watch DataUpload CRs for Volume Snapshot Data Movement backups, so there is one controller instance in each node. One controller instance takes a DataUpload CR and then launches a VGDP instance, which initializes a uploader instance and the backup repository connection, to finish the data transfer. The VGDP instance runs inside a node-agent pod or in a pod associated to the node-agent pod in the same node.
|
||||
|
||||
Varying from the data size, data complexity, resource availability, VGDP may take a long time and remarkable resources (CPU, memory, network bandwidth, etc.).
|
||||
Technically, VGDP instances are able to run in any node that allows pod schedule. On the other hand, users may want to constrain the nodes where VGDP instances run for various reasons, below are some examples:
|
||||
- Prevent VGDP instances from running in specific nodes because users have more critical workloads in the nodes
|
||||
- Constrain VGDP instances to run in specific nodes because these nodes have more resources than others
|
||||
- Constrain VGDP instances to run in specific nodes because the storage allows volume/snapshot provisions in these nodes only
|
||||
|
||||
Therefore, in order to improve the compatibility, it is worthy to configure the affinity of VGDP to nodes, especially for backups for which VGDP instances run frequently and centrally.
|
||||
|
||||
## Goals
|
||||
|
||||
- Define the behaviors of node affinity of VGDP instances in node-agent for volume snapshot data movement backups
|
||||
- Create a mechanism for users to specify the node affinity of VGDP instances for volume snapshot data movement backups
|
||||
|
||||
## Non-Goals
|
||||
- It is also beneficial to support VGDP instances affinity for PodVolume backup/restore, however, it is not possible since VGDP instances for PodVolume backup/restore should always run in the node where the source/target pods are created.
|
||||
- It is also beneficial to support VGDP instances affinity for data movement restores, however, it is not possible in some cases. For example, when the `volumeBindingMode` in the storageclass is `WaitForFirstConsumer`, the restore volume must be mounted in the node where the target pod is scheduled, so the VGDP instance must run in the same node. On the other hand, considering the fact that restores may not frequently and centrally run, we will not support data movement restores.
|
||||
- As elaberated in the [Volume Snapshot Data Movement Design][2], the Exposer may take different ways to expose snapshots, i.e., through backup pods (this is the only way supported at present). The implementation section below only considers this approach currently, if a new expose method is introduced in future, the definition of the affinity configurations and behaviors should still work, but we may need a new implementation.
|
||||
|
||||
## Solution
|
||||
|
||||
We will use the ```node-agent-config``` configMap to host the node affinity configurations.
|
||||
This configMap is not created by Velero, users should create it manually on demand. The configMap should be in the same namespace where Velero is installed. If multiple Velero instances are installed in different namespaces, there should be one configMap in each namespace which applies to node-agent in that namespace only.
|
||||
Node-agent server checks these configurations at startup time and use it to initiate the related VGDP modules. Therefore, users could edit this configMap any time, but in order to make the changes effective, node-agent server needs to be restarted.
|
||||
Inside ```node-agent-config``` configMap we will add one new kind of configuration as the data in the configMap, the name is ```loadAffinity```.
|
||||
Users may want to set different LoadAffinity configurations according to different conditions (i.e., for different storages represented by StorageClass, CSI driver, etc.), so we define ```loadAffinity``` as an array. This is for extensibility consideration, at present, we don't implement multiple configurations support, so if there are multiple configurations, we always take the first one in the array.
|
||||
|
||||
The data structure for ```node-agent-config``` is as below:
|
||||
```go
|
||||
type Configs struct {
|
||||
// LoadConcurrency is the config for load concurrency per node.
|
||||
LoadConcurrency *LoadConcurrency `json:"loadConcurrency,omitempty"`
|
||||
|
||||
// LoadAffinity is the config for data path load affinity.
|
||||
LoadAffinity []*LoadAffinity `json:"loadAffinity,omitempty"`
|
||||
}
|
||||
|
||||
type LoadAffinity struct {
|
||||
// NodeSelector specifies the label selector to match nodes
|
||||
NodeSelector metav1.LabelSelector `json:"nodeSelector"`
|
||||
}
|
||||
```
|
||||
|
||||
### Affinity
|
||||
Affinity configuration means allowing VGDP instances running in the nodes specified. There are two ways to define it:
|
||||
- It could be defined by `MatchLabels` of `metav1.LabelSelector`. The labels defined in `MatchLabels` means a `LabelSelectorOpIn` operation by default, so in the current context, they will be treated as affinity rules.
|
||||
- It could be defined by `MatchExpressions` of `metav1.LabelSelector`. The labels are defined in `Key` and `Values` of `MatchExpressions` and the `Operator` should be defined as `LabelSelectorOpIn` or `LabelSelectorOpExists`.
|
||||
|
||||
### Anti-affinity
|
||||
Anti-affinity configuration means preventing VGDP instances running in the nodes specified. Below is the way to define it:
|
||||
- It could be defined by `MatchExpressions` of `metav1.LabelSelector`. The labels are defined in `Key` and `Values` of `MatchExpressions` and the `Operator` should be defined as `LabelSelectorOpNotIn` or `LabelSelectorOpDoesNotExist`.
|
||||
|
||||
### Sample
|
||||
A sample of the ```node-agent-config``` configMap is as below:
|
||||
```json
|
||||
{
|
||||
"loadAffinity": [
|
||||
{
|
||||
"nodeSelector": {
|
||||
"matchLabels": {
|
||||
"beta.kubernetes.io/instance-type": "Standard_B4ms"
|
||||
},
|
||||
"matchExpressions": [
|
||||
{
|
||||
"key": "kubernetes.io/hostname",
|
||||
"values": [
|
||||
"node-1",
|
||||
"node-2",
|
||||
"node-3"
|
||||
],
|
||||
"operator": "In"
|
||||
},
|
||||
{
|
||||
"key": "xxx/critial-workload",
|
||||
"operator": "DoesNotExist"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
This sample showcases two affinity configurations:
|
||||
- matchLabels: VGDP instances will run only in nodes with label key `beta.kubernetes.io/instance-type` and value `Standard_B4ms`
|
||||
- matchExpressions: VGDP instances will run in node `node1`, `node2` and `node3` (selected by `kubernetes.io/hostname` label)
|
||||
|
||||
This sample showcases one anti-affinity configuration:
|
||||
- matchExpressions: VGDP instances will not run in nodes with label key `xxx/critial-workload`
|
||||
|
||||
To create the configMap, users need to save something like the above sample to a json file and then run below command:
|
||||
```
|
||||
kubectl create cm node-agent-config -n velero --from-file=<json file name>
|
||||
```
|
||||
|
||||
### Implementation
|
||||
As mentioned in the [Volume Snapshot Data Movement Design][2], the exposer decides where to launch the VGDP instances. At present, for volume snapshot data movement backups, the exposer creates backupPods and the VGDP instances will be initiated in the nodes where backupPods are scheduled. So the loadAffinity will be translated (from `metav1.LabelSelector` to `corev1.Affinity`) and set to the backupPods.
|
||||
|
||||
It is possible that node-agent pods, as a daemonset, don't run in every worker nodes, users could fulfil this by specify `nodeSelector` or `nodeAffinity` to the node-agent daemonset spec. On the other hand, at present, VGDP instances must be assigned to nodes where node-agent pods are running. Therefore, if there is any node selection for node-agent pods, users must consider this into this load affinity configuration, so as to guarantee that VGDP instances are always assigned to nodes where node-agent pods are available. This is done by users, we don't inherit any node selection configuration from node-agent daemonset as we think daemonset scheduler works differently from plain pod scheduler, simply inheriting all the configurations may cause unexpected result of backupPod schedule.
|
||||
Otherwise, if a backupPod are scheduled to a node where node-agent pod is absent, the corresponding DataUpload CR will stay in `Accepted` phase until the prepare timeout (by default 30min).
|
||||
|
||||
At present, as part of the expose operations, the exposer creates a volume, represented by backupPVC, from the snapshot. The backupPVC uses the same storageClass with the source volume. If the `volumeBindingMode` in the storageClass is `Immediate`, the volume is immediately allocated from the underlying storage without waiting for the backupPod. On the other hand, the loadAffinity is set to the backupPod's affinity. If the backupPod is scheduled to a node where the snapshot volume is not accessible, e.g., because of storage topologies, the backupPod won't get into Running state, concequently, the data movement won't complete.
|
||||
Once this problem happens, the backupPod stays in `Pending` phase, and the corresponding DataUpload CR stays in `Accepted` phase until the prepare timeout (by default 30min).
|
||||
|
||||
There is a common solution for the both problems:
|
||||
- We have an existing logic to periodically enqueue the dataupload CRs which are in the `Accepted` phase for timeout and cancel checks
|
||||
- We add a new logic to this existing logic to check if the corresponding backupPods are in unrecoverable status
|
||||
- The above problems could be covered by this check, because in both cases the backupPods are in abnormal and unrecoverable status
|
||||
- If a backupPod is unrecoverable, the dataupload controller cancels the dataupload and deletes the backupPod
|
||||
|
||||
Specifically, when the above problems happen, the status of a backupPod is like below:
|
||||
```
|
||||
status:
|
||||
conditions:
|
||||
- lastProbeTime: null
|
||||
message: '0/2 nodes are available: 1 node(s) didn''t match Pod''s node affinity/selector,
|
||||
1 node(s) had volume node affinity conflict. preemption: 0/2 nodes are available:
|
||||
2 Preemption is not helpful for scheduling..'
|
||||
reason: Unschedulable
|
||||
status: "False"
|
||||
type: PodScheduled
|
||||
phase: Pending
|
||||
```
|
||||
|
||||
[1]: Implemented/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md
|
||||
[2]: volume-snapshot-data-movement/volume-snapshot-data-movement.md
|
||||
@@ -1,131 +0,0 @@
|
||||
# Node-agent Concurrency Design
|
||||
|
||||
## Glossary & Abbreviation
|
||||
|
||||
**Velero Generic Data Path (VGDP)**: VGDP is the collective of modules that is introduced in [Unified Repository design][1]. Velero uses these modules to finish data transfer for various purposes (i.e., PodVolume backup/restore, Volume Snapshot Data Movement). VGDP modules include uploaders and the backup repository.
|
||||
|
||||
## Background
|
||||
|
||||
Velero node-agent is a daemonset hosting controllers and VGDP modules to complete the concrete work of backups/restores, i.e., PodVolume backup/restore, Volume Snapshot Data Movement backup/restore.
|
||||
For example, node-agent runs DataUpload controllers to watch DataUpload CRs for Volume Snapshot Data Movement backups, so there is one controller instance in each node. One controller instance takes a DataUpload CR and then launches a VGDP instance, which initializes a uploader instance and the backup repository connection, to finish the data transfer. The VGDP instance runs inside the node-agent pod or in a pod associated to the node-agent pod in the same node.
|
||||
|
||||
Varying from the data size, data complexity, resource availability, VGDP may take a long time and remarkable resources (CPU, memory, network bandwidth, etc.).
|
||||
Technically, VGDP instances are able to run in concurrent regardless of the requesters. For example, a VGDP instance for a PodVolume backup could run in parallel with another VGDP instance for a DataUpload. Then the two VGDP instances share the same resources if they are running in the same node.
|
||||
|
||||
Therefore, in order to gain the optimized performance with the limited resources, it is worthy to configure the concurrent number of VGDP per node. When the resources are sufficient in nodes, users can set a large concurrent number, so as to reduce the backup/restore time; otherwise, the concurrency should be reduced, otherwise, the backup/restore may encounter problems, i.e., time lagging, hang or OOM kill.
|
||||
|
||||
## Goals
|
||||
|
||||
- Define the behaviors of concurrent VGDP instances in node-agent
|
||||
- Create a mechanism for users to specify the concurrent number of VGDP per node
|
||||
|
||||
## Non-Goals
|
||||
- VGDP instances from different nodes always run in concurrent since in most common cases the resources are isolated. For special cases that some resources are shared across nodes, there is no support at present
|
||||
- In practice, restores run in prioritized scenarios, e.g., disaster recovery. However, the current design doesn't consider this difference, a VGDP instance for a restore is blocked if it reaches to the limit of the concurrency, even though the ones block it are for backups. If users do meet some problems here, they should consider to stop the backups first
|
||||
- Sometimes, users wants to totally block backups/restores from running in a specific node, this is out of the scope the current design. To archive this, more modules need to be considered (i.e., expoers of data movers), simply blocking the VGDP (e.g., by setting its concurrent number to 0) doesn't work. E.g., for a fs backup, VGDP instance must run in the node the source pod is running in, if we simply block from VGDP instance, the PodVolumeBackup CR is still submitted but never processed.
|
||||
|
||||
## Solution
|
||||
|
||||
We introduce a configMap named ```node-agent-config``` for users to specify the node-agent related configurations. This configMap is not created by Velero, users should create it manually on demand. The configMap should be in the same namespace where Velero is installed. If multiple Velero instances are installed in different namespaces, there should be one configMap in each namespace which applies to node-agent in that namespace only.
|
||||
Node-agent server checks these configurations at startup time and use it to initiate the related VGDP modules. Therefore, users could edit this configMap any time, but in order to make the changes effective, node-agent server needs to be restarted.
|
||||
The ```node-agent-config``` configMap may be used for other purpose of configuring node-agent in future, at present, there is only one kind of configuration as the data in the configMap, the name is ```loadConcurrency```.
|
||||
|
||||
The data structure for ```node-agent-config``` is as below:
|
||||
```go
|
||||
type Configs struct {
|
||||
// LoadConcurrency is the config for load concurrency per node.
|
||||
LoadConcurrency *LoadConcurrency `json:"loadConcurrency,omitempty"`
|
||||
}
|
||||
|
||||
type LoadConcurrency struct {
|
||||
// GlobalConfig specifies the concurrency number to all nodes for which per-node config is not specified
|
||||
GlobalConfig int `json:"globalConfig,omitempty"`
|
||||
|
||||
// PerNodeConfig specifies the concurrency number to nodes matched by rules
|
||||
PerNodeConfig []RuledConfigs `json:"perNodeConfig,omitempty"`
|
||||
}
|
||||
|
||||
type RuledConfigs struct {
|
||||
// NodeSelector specifies the label selector to match nodes
|
||||
NodeSelector metav1.LabelSelector `json:"nodeSelector"`
|
||||
|
||||
// Number specifies the number value associated to the matched nodes
|
||||
Number int `json:"number"`
|
||||
}
|
||||
```
|
||||
|
||||
### Global concurrent number
|
||||
We allow users to specify a concurrent number that will be applied to all nodes if the per-node number is not specified. This number is set through ```globalConfig```.
|
||||
The number starts from 1 which means there is no concurrency, only one instance of VGDP is allowed. There is no roof limit.
|
||||
If this number is not specified or not valid, a hard-coded default value will be used, the value is set to 1.
|
||||
|
||||
### Per-node concurrent number
|
||||
We allow users to specify different concurrent number per node, for example, users can set 3 concurrent instances in Node-1, 2 instances in Node-2 and 1 instance in Node-3. This is for below considerations:
|
||||
- The resources may be different among nodes. Then users could specify smaller concurrent number for nodes with less resources while larger number for the ones with more resources
|
||||
- Help users to isolate critical environments. Users may run some critical workloads in some specified nodes, since VGDP instances may take large resource consumption, users may want to run less number of instances in the nodes with critical workloads
|
||||
|
||||
The range of Per-node concurrent number is the same with Global concurrent number.
|
||||
Per-node concurrent number is preferable to Global concurrent number, so it will overwrite the Global concurrent number for that node.
|
||||
|
||||
Per-node concurrent number is implemented through ```perNodeConfig``` field.
|
||||
|
||||
```perNodeConfig``` is a list of ```RuledConfigs``` each item of which matches one or more nodes by label selectors and specify the concurrent number for the matched nodes. This means, the nodes are identified by labels.
|
||||
|
||||
For example, the ```perNodeConfig`` could have below elements:
|
||||
```
|
||||
"nodeSelector: kubernetes.io/hostname=node1; number: 3"
|
||||
"nodeSelector: beta.kubernetes.io/instance-type=Standard_B4ms; number: 5"
|
||||
```
|
||||
The first element means the node with host name ```node1``` gets the Per-node concurrent number of 3.
|
||||
The second element means all the nodes with label ```beta.kubernetes.io/instance-type``` of value ```Standard_B4ms``` get the Per-node concurrent number of 5.
|
||||
At least one node is expected to have a label with the specified ```RuledConfigs``` element (rule). If no node is with this label, the Per-node rule makes no effect.
|
||||
If one node falls into more than one rules, e.g., if node1 also has the label ```beta.kubernetes.io/instance-type=Standard_B4ms```, the smallest number (3) will be used.
|
||||
|
||||
### Sample
|
||||
A sample of the ```node-agent-config``` configMap is as below:
|
||||
```json
|
||||
{
|
||||
"loadConcurrency": {
|
||||
"globalConfig": 2,
|
||||
"perNodeConfig": [
|
||||
{
|
||||
"nodeSelector": {
|
||||
"matchLabels": {
|
||||
"kubernetes.io/hostname": "node1"
|
||||
}
|
||||
},
|
||||
"number": 3
|
||||
},
|
||||
{
|
||||
"nodeSelector": {
|
||||
"matchLabels": {
|
||||
"beta.kubernetes.io/instance-type": "Standard_B4ms"
|
||||
}
|
||||
},
|
||||
"number": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
To create the configMap, users need to save something like the above sample to a json file and then run below command:
|
||||
```
|
||||
kubectl create cm node-agent-config -n velero --from-file=<json file name>
|
||||
```
|
||||
|
||||
### Global data path manager
|
||||
As for the code implementation, data path manager is to maintain the total number of the running VGDP instances and ensure the limit is not excceeded. At present, there is one data path manager instance per controller, as a result, the concurrent numbers are calculated separately for each controller. This doesn't help to limit the concurrency among different requesters.
|
||||
Therefore, we need to create one global data path manager instance server-wide, and pass it to different controllers. The instance will be created at node-agent server startup.
|
||||
The concurrent number is required to initiate a data path manager, the number comes from either Per-node concurrent number or Global concurrent number.
|
||||
Below are some prototypes related to data path manager:
|
||||
|
||||
```go
|
||||
func NewManager(cocurrentNum int) *Manager
|
||||
func (m *Manager) CreateFileSystemBR(jobName string, requestorType string, ctx context.Context, client client.Client, namespace string, callbacks Callbacks, log logrus.FieldLogger) (AsyncBR, error)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[1]: Implemented/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md
|
||||
@@ -1,186 +0,0 @@
|
||||
# PersistentVolume backup information design
|
||||
|
||||
## Abstract
|
||||
Create a new metadata file in the backup repository's backup name sub-directory to store the backup-including PVC and PV information. The information includes the way of backing up the PVC and PV data, snapshot information, and status. The needed snapshot status can also be recorded there, but the Velero-Native snapshot plugin doesn't provide a way to get the snapshot size from the API, so it's possible that not all snapshot size information is available.
|
||||
|
||||
This new additional metadata file is needed when:
|
||||
* Get a summary of the backup's PVC and PV information, including how the data in them is backed up, or whether the data in them is skipped from backup.
|
||||
* Find out how the PVC and PV should be restored in the restore process.
|
||||
* Retrieve the PV's snapshot information for backup.
|
||||
|
||||
## Background
|
||||
There is already a [PR](https://github.com/vmware-tanzu/velero/pull/6496) to track the skipped PVC in the backup. This design will depend on it and go further to get a summary of PVC and PV information, then persist into a metadata file in the backup repository.
|
||||
|
||||
In the restore process, the Velero server needs to decide how the PV resource should be restored according to how the PV is backed up. The current logic is to check whether it's backed up by Velero-native snapshot, by file-system backup, or having `DeletionPolicy` set as `Delete`.
|
||||
|
||||
The checks are made by the backup-generated PVBs or Snapshots. There is no generic way to find this information, and the CSI backup and Snapshot data movement backup are not covered.
|
||||
|
||||
Another thing that needs noticing is when describing the backup, there is no generic way to find the PV's snapshot information.
|
||||
|
||||
## Goals
|
||||
- Create a new metadata file to store backup's PVCs and PVs information and volume data backing up method. The file can be used to let downstream consumers generate a summary.
|
||||
- Create a generic way to let the Velero server know how the PV resources are backed up.
|
||||
- Create a generic way to let the Velero server find the PV corresponding snapshot information.
|
||||
|
||||
## Non Goals
|
||||
- Unify how to get snapshot size information for all PV backing-up methods, and all other currently not ready PVs' information.
|
||||
|
||||
## High-Level Design
|
||||
Create _backup-name_-volumes-info.json metadata file in the backup's repository. This file will be encoded to contain all the PVC and PV information included in the backup. The information covers whether the PV or PVC's data is skipped during backup, how its data is backed up, and the backed-up detail information.
|
||||
|
||||
Please notice that the new metadata file includes all skipped volume information. This is used to address [the second phase needs of skipped volumes information](https://github.com/vmware-tanzu/velero/issues/5834#issuecomment-1526624211).
|
||||
|
||||
The `restoreItem` function can decode the _backup-name_-volumes-info.json file to determine how to handle the PV resource.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### The VolumeInfo structure
|
||||
_backup-name_-volumes-info.json file is a structure that contains an array of structure `VolumeInfo`.
|
||||
|
||||
``` golang
|
||||
type VolumeInfo struct {
|
||||
PVCName string // The PVC's name.
|
||||
PVCNamespace string // The PVC's namespace.
|
||||
PVName string // The PV name.
|
||||
BackupMethod string // The way the volume data is backed up. The valid value includes `VeleroNativeSnapshot`, `PodVolumeBackup` and `CSISnapshot`.
|
||||
SnapshotDataMoved bool // Whether the volume's snapshot data is moved to specified storage.
|
||||
|
||||
Skipped boolean // Whether the Volume is skipped in this backup.
|
||||
SkippedReason string // The reason for the volume is skipped in the backup.
|
||||
StartTimestamp *metav1.Time // Snapshot starts timestamp.
|
||||
|
||||
OperationID string // The Async Operation's ID.
|
||||
|
||||
CSISnapshotInfo CSISnapshotInfo
|
||||
SnapshotDataMovementInfo SnapshotDataMovementInfo
|
||||
NativeSnapshotInfo VeleroNativeSnapshotInfo
|
||||
PVBInfo PodVolumeBackupInfo
|
||||
PVInfo PVInfo
|
||||
}
|
||||
|
||||
// CSISnapshotInfo is used for displaying the CSI snapshot status
|
||||
type CSISnapshotInfo struct {
|
||||
SnapshotHandle string // It's the storage provider's snapshot ID for CSI.
|
||||
Size int64 // The snapshot corresponding volume size.
|
||||
|
||||
Driver string // The name of the CSI driver.
|
||||
VSCName string // The name of the VolumeSnapshotContent.
|
||||
}
|
||||
|
||||
// SnapshotDataMovementInfo is used for displaying the snapshot data mover status.
|
||||
type SnapshotDataMovementInfo struct {
|
||||
DataMover string // The data mover used by the backup. The valid values are `velero` and ``(equals to `velero`).
|
||||
UploaderType string // The type of the uploader that uploads the snapshot data. The valid values are `kopia` and `restic`.
|
||||
RetainedSnapshot string // The name or ID of the snapshot associated object(SAO). SAO is used to support local snapshots for the snapshot data mover, e.g. it could be a VolumeSnapshot for CSI snapshot data moign/pv_backup_info.
|
||||
SnapshotHandle string // It's the filesystem repository's snapshot ID.
|
||||
|
||||
}
|
||||
|
||||
// VeleroNativeSnapshotInfo is used for displaying the Velero native snapshot status.
|
||||
type VeleroNativeSnapshotInfo struct {
|
||||
SnapshotHandle string // It's the storage provider's snapshot ID for the Velero-native snapshot.
|
||||
|
||||
VolumeType string // The cloud provider snapshot volume type.
|
||||
VolumeAZ string // The cloud provider snapshot volume's availability zones.
|
||||
IOPS string // The cloud provider snapshot volume's IOPS.
|
||||
}
|
||||
|
||||
// PodVolumeBackupInfo is used for displaying the PodVolumeBackup snapshot status.
|
||||
type PodVolumeBackupInfo struct {
|
||||
SnapshotHandle string // It's the file-system uploader's snapshot ID for PodVolumeBackup.
|
||||
Size int64 // The snapshot corresponding volume size.
|
||||
|
||||
UploaderType string // The type of the uploader that uploads the data. The valid values are `kopia` and `restic`.
|
||||
VolumeName string // The PVC's corresponding volume name used by Pod: https://github.com/kubernetes/kubernetes/blob/e4b74dd12fa8cb63c174091d5536a10b8ec19d34/pkg/apis/core/types.go#L48
|
||||
PodName string // The Pod name mounting this PVC.
|
||||
PodNamespace string // The Pod namespace.
|
||||
NodeName string // The PVB-taken k8s node's name.
|
||||
}
|
||||
|
||||
// PVInfo is used to store some PV information modified after creation.
|
||||
// Those information are lost after PV recreation.
|
||||
type PVInfo struct {
|
||||
ReclaimPolicy string // ReclaimPolicy of PV. It could be different from the referenced StorageClass.
|
||||
Labels map[string]string // The PV's labels should be kept after recreation.
|
||||
}
|
||||
```
|
||||
|
||||
### How the VolumeInfo array is generated.
|
||||
The function `persistBackup` has `backup *pkgbackup.Request` in parameters.
|
||||
From it, the `VolumeSnapshots`, `PodVolumeBackups`, `CSISnapshots`, `itemOperationsList`, and `SkippedPVTracker` can be read. All of them will be iterated and merged into the `VolumeInfo` array, and then persisted into backup repository in function `persistBackup`.
|
||||
|
||||
Please notice that the change happened in async operations are not reflected in the new metadata file. The file only covers the volume changes happen in the Velero server process scope.
|
||||
|
||||
A new methods are added to BackupStore to download the VolumeInfo metadata file.
|
||||
Uploading the metadata file is covered in the exiting `PutBackup` method.
|
||||
|
||||
``` golang
|
||||
type BackupStore interface {
|
||||
...
|
||||
GetVolumeInfos(name string) ([]*VolumeInfo, error)
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### How the VolumeInfo array is used.
|
||||
|
||||
#### Generate the PVC backed-up information summary
|
||||
The downstream tools can use this VolumeInfo array to format and display their volume information. This is not in the scope of this feature.
|
||||
|
||||
#### Retrieve volume backed-up information for `velero backup describe` command
|
||||
The `velero backup describe` can also use this VolumeInfo array structure to display the volume information. The snapshot data mover volume should use this structure at first, then the Velero native snapshot, CSI snapshot, and PodVolumeBackup can also use this structure. The detailed implementation is also not in this feature's scope.
|
||||
|
||||
#### Let restore know how to restore the PV
|
||||
In the function `restoreItem`, it will determine whether to restore the PV resource by checking it in the Velero native snapshots list, PodVolumeBackup list, and its DeletionPolicy. This logic is still kept. The logic will be used when the new `VolumeInfo` metadata cannot be found to support backward compatibility.
|
||||
|
||||
``` golang
|
||||
if groupResource == kuberesource.PersistentVolumes {
|
||||
switch {
|
||||
case hasSnapshot(name, ctx.volumeSnapshots):
|
||||
...
|
||||
case hasPodVolumeBackup(obj, ctx):
|
||||
...
|
||||
case hasDeleteReclaimPolicy(obj.Object):
|
||||
...
|
||||
default:
|
||||
...
|
||||
```
|
||||
|
||||
After introducing the VolumeInfo array, the following logic will be added.
|
||||
``` golang
|
||||
if groupResource == kuberesource.PersistentVolumes {
|
||||
volumeInfo := GetVolumeInfo(pvName)
|
||||
switch volumeInfo.BackupMethod {
|
||||
case VeleroNativeSnapshot:
|
||||
...
|
||||
case PodVolumeBackup:
|
||||
...
|
||||
case CSISnapshot:
|
||||
...
|
||||
default:
|
||||
// Need to check whether the volume is backed up by the SnapshotDataMover.
|
||||
if volumeInfo.SnapshotDataMovement:
|
||||
|
||||
// Check whether the Velero server should restore the PV depending on the DeletionPolicy setting.
|
||||
if volumeInfo.Skipped:
|
||||
```
|
||||
|
||||
### How the VolumeInfo metadata file is deleted
|
||||
_backup-name_-volumes-info.json file is deleted during backup deletion.
|
||||
|
||||
## Alternatives Considered
|
||||
The restore process needs more information about how the PVs are backed up to determine whether this PV should be restored. The released branches also need a similar function, but backporting a new feature into previous releases may not be a good idea, so according to [Anshul Ahuja's suggestion](https://github.com/vmware-tanzu/velero/issues/6595#issuecomment-1731081580), adding more cases here to support checking PV backed-up by CSI plugin and CSI snapshot data mover: https://github.com/vmware-tanzu/velero/blob/5ff5073cc3f364bafcfbd26755e2a92af68ba180/pkg/restore/restore.go#L1206-L1324.
|
||||
|
||||
## Security Considerations
|
||||
There should be no security impact introduced by this design.
|
||||
|
||||
## Compatibility
|
||||
After this design is implemented, there should be no impact on the existing [skipped PVC summary feature](https://github.com/vmware-tanzu/velero/pull/6496).
|
||||
|
||||
To support older version backup, which doesn't have the VolumeInfo metadata file, the old logic, which is checking the Velero native snapshots list, PodVolumeBackup list, and PVC DeletionPolicy, is still kept, and supporting CSI snapshots and snapshot data mover logic will be added too.
|
||||
|
||||
## Implementation
|
||||
This will be implemented in the Velero v1.13 development cycle.
|
||||
|
||||
## Open Issues
|
||||
There are no open issues identified by now.
|
||||
@@ -1,143 +0,0 @@
|
||||
# Volume information for restore design
|
||||
|
||||
## Background
|
||||
Velero has different ways to handle data in the volumes during restore. The users want to have more clarity in terms of how
|
||||
the volumes are handled in restore process via either Velero CLI or other downstream product which consumes Velero.
|
||||
|
||||
## Goals
|
||||
- Create new metadata to store the information of the restored volume, which will have the same life-cycle as the restore CR.
|
||||
- Consume the metadata in velero CLI to enable it display more details for volumes in the output of `velero restore describe --details`
|
||||
|
||||
## Non Goals
|
||||
- Provide finer grained control of the volume restore process. The focus of the design is to enable displaying more details.
|
||||
- Persist additional metadata like podvolume, datadownloads etc to the restore folder in backup-location.
|
||||
|
||||
## Design
|
||||
|
||||
### Structure of the restore volume info
|
||||
The restore volume info will be stored in a file named like `${restore_name}-vol-info.json`. The content of the file will
|
||||
be a list of volume info objects, each of which will map to a volume that is restored, and will contain the information
|
||||
like name of the restored PV/PVC, restore method and related objects to provide details depending on the way it's restored,
|
||||
it will look like this:
|
||||
```
|
||||
[
|
||||
{
|
||||
"pvcName": "nginx-logs-2",
|
||||
"pvcNamespace": "nginx-app-restore",
|
||||
"pvName": "pvc-e320d75b-a788-41a3-b6ba-267a553efa5e",
|
||||
"restoreMethod": "PodVolumeRestore",
|
||||
"snapshotDataMoved": false,
|
||||
"pvrInfo": {
|
||||
"snapshotHandle": "81973157c3a945a5229285c931b02c68",
|
||||
"uploaderType": "kopia",
|
||||
"volumeName": "nginx-logs",
|
||||
"podName": "nginx-deployment-79b56c644b-mjdhp",
|
||||
"podNamespace": "nginx-app-restore"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pvcName": "nginx-logs-1",
|
||||
"pvcNamespace": "nginx-app-restore",
|
||||
"pvName": "pvc-98c151f4-df47-4980-ba6d-470842f652cc",
|
||||
"restoreMethod": "CSISnapshot",
|
||||
"snapshotDataMoved": false,
|
||||
"csiSnapshotInfo": {
|
||||
"snapshotHandle": "snap-01a3b21a5e9f85528",
|
||||
"size": 2147483648,
|
||||
"driver": "ebs.csi.aws.com",
|
||||
"vscName": "velero-velero-nginx-logs-1-jxmbg-hx9x5"
|
||||
}
|
||||
}
|
||||
......
|
||||
]
|
||||
```
|
||||
Each field will have the same meaning as the corresponding field in the backup volume info. It will not have the fields
|
||||
that were introduced to help with the backup process, like `pvInfo`, `dataupload` etc.
|
||||
|
||||
### How the restore volume info is generated
|
||||
Two steps are involved in generating the restore volume info, the first is "collection", which is to gather the information
|
||||
for restoration of the volumes, the second is "generation", which is to iterate through the data collected in the first step
|
||||
and generate the volume info list as is described above.
|
||||
|
||||
Unlike backup, the CR objects created during the restore process will not be persisted to the backup storage location.
|
||||
Therefore, to gather the information needed to generate volume information, we either need to collect the CRs in the middle
|
||||
of the restore process, or retrieve the objects based on the `resouce-list.json` of the restore via API server.
|
||||
The information to be collected are:
|
||||
- **PV/PVC mapping relationship:** It will be collected via the `restore-resource-list.json`, b/c at the time the json is ready, all
|
||||
PVCs and PVs are already created.
|
||||
- **Native snapshot information:** It will be collected in the restore workflow when each snapshot is restored.
|
||||
- **podvolumerestore CRs:** It will be collected in the restore workflow after each pvr is created.
|
||||
- **volumesnapshot CRs for CSI snapshot:** It will be collected in the step of collecting PVC info, by reading the `dataSource`
|
||||
field in the spec of the PVC.
|
||||
- **datadownload CRs** It will be collected in the phase of collecting PVC info, by querying the API-server to list the datadownload
|
||||
CRs labeled with the restore name.
|
||||
|
||||
After the collection step, the generation step is relatively straight-forward, as we have all the information needed in
|
||||
the data structures.
|
||||
|
||||
The whole collection and generation steps will be done with the "best-effort" manner, i.e. if there are any failures we
|
||||
will only log the error in restore log, rather than failing the whole restore process, we will not put these errors or warnings
|
||||
into the `result.json`, b/c it won't impact the restored resources.
|
||||
|
||||
Depending on the number of the restored PVCs the "collection" step may involve many API calls, but it's considered acceptable
|
||||
b/c at that time the resources are already created, so the actual RTO is not impacted. By using the client of controller runtime
|
||||
we can make the collection step more efficient by using the cache of the API server. We may consider to make improvements if
|
||||
we observe performance issues, like using multiple go-routines in the collection.
|
||||
|
||||
### Implementation
|
||||
Because the restore volume info shares the same data structures with the backup volume info, we will refactor the code in
|
||||
package `internal/volume` to make the sub-components in backup volume info shared by both backup and restore volume info.
|
||||
|
||||
We'll introduce a struct called `RestoreVolumeInfoTracker` which encapsulates the logic of collecting and generating the restore volume info:
|
||||
```
|
||||
// RestoreVolumeInfoTracker is used to track the volume information during restore.
|
||||
// It is used to generate the RestoreVolumeInfo array.
|
||||
type RestoreVolumeInfoTracker struct {
|
||||
*sync.Mutex
|
||||
restore *velerov1api.Restore
|
||||
log logrus.FieldLogger
|
||||
client kbclient.Client
|
||||
pvPvc *pvcPvMap
|
||||
|
||||
// map of PV name to the NativeSnapshotInfo from which the PV is restored
|
||||
pvNativeSnapshotMap map[string]NativeSnapshotInfo
|
||||
// map of PV name to the CSISnapshot object from which the PV is restored
|
||||
pvCSISnapshotMap map[string]snapshotv1api.VolumeSnapshot
|
||||
datadownloadList *velerov2alpha1.DataDownloadList
|
||||
pvrs []*velerov1api.PodVolumeRestore
|
||||
}
|
||||
```
|
||||
The `RestoreVolumeInfoTracker` will be created when the restore request is initialized, and it will be passed to the `restoreContext`
|
||||
and carried over the whole restore process.
|
||||
|
||||
The `client` in this struct is to be used to query the resources in the restored namespace, and the current client in restore
|
||||
reconciler only watches the resources in the namespace where velero is installed. Therefore, we need to introduce the
|
||||
`CrClient` which has the same life-cycle of velero server to the restore reconciler, because this is the client that watches all the
|
||||
resources on the cluster.
|
||||
|
||||
In addition to that, we will make small changes in the restore workflow to collect the information needed. We'll make the
|
||||
changes un-intrusive and make sure not to change the logic of the restore to avoid break change or regression.
|
||||
We'll also introduce routine changes in the package `pkg/persistence` to persist the restore volume info to the backup storage location.
|
||||
|
||||
Last but not least, the `velero restore describe --details` will be updated to display the volume info in the output.
|
||||
|
||||
## Alternatives Considered
|
||||
There used to be suggestion that to provide more details about volume, we can query the `backup-vol-info.json` with the resource
|
||||
identifier in `restore-resource-list.json`. This will not work when there're resource modifiers involved in the restore process,
|
||||
which may change the metadata of PVC/PV. In addition, we may add more detailed restore-specific information about the volumes that is not available
|
||||
in the `backup-vol-info.json`. Therefore, the `restore-vol-info.json` is a better approach.
|
||||
|
||||
## Security Considerations
|
||||
There should be no security impact introduced by this design.
|
||||
|
||||
## Compatibility
|
||||
The restore volume info will be consumed by Velero CLI and downstream products for displaying details. So the functionality
|
||||
of backup and restore will not be impacted for restores created by older versions of Velero which do not have the restore volume info
|
||||
metadata. The client should properly handle the case when the restore volume info does not exist.
|
||||
|
||||
The data structures referenced by volume info is shared between both restore and backup and it's not versioned, so in the future
|
||||
we must make sure there will only be incremental changes to the metadata, such that no break change will be introduced to the client.
|
||||
|
||||
## Open Issues
|
||||
https://github.com/vmware-tanzu/velero/issues/7546
|
||||
https://github.com/vmware-tanzu/velero/issues/6478
|
||||
@@ -1,318 +0,0 @@
|
||||
# Design for repository maintenance job
|
||||
|
||||
## Abstract
|
||||
This design proposal aims to decouple repository maintenance from the Velero server by launching a maintenance job when needed, to mitigate the impact on the Velero server during backups.
|
||||
|
||||
## Background
|
||||
During backups, Velero performs periodic maintenance on the repository. This operation may consume significant CPU and memory resources in some cases, leading to potential issues such as the Velero server being killed by OOM. This proposal addresses these challenges by separating repository maintenance from the Velero server.
|
||||
|
||||
## Goals
|
||||
1. **Independent Repository Maintenance**: Decouple maintenance from Velero's main logic to reduce the impact on the Velero server pod.
|
||||
|
||||
2. **Configurable Resources Usage**: Make the resources used by the maintenance job configurable.
|
||||
|
||||
3. **No API Changes**: Retain existing APIs and workflow in the backup repository controller.
|
||||
|
||||
## Non Goals
|
||||
We have lots of concerns over parallel maintenance, which will increase the complexity of our design currently.
|
||||
|
||||
- Non-blocking maintenance job: it may conflict with updating the same `backuprepositories` CR when parallel maintenance.
|
||||
|
||||
- Maintenance job concurrency control: there is no one suitable mechanism in Kubernetes to control the concurrency of different jobs.
|
||||
|
||||
- Parallel maintenance: Maintaining the same repo by multiple jobs at the same time would have some compatible cases that some providers may not support.
|
||||
|
||||
Unfortunately, parallel maintenance is currently not a priority because of the concerns above, improving maintenance efficiency is not the primary focus at this stage.
|
||||
|
||||
## High-Level Design
|
||||
1. **Add Maintenance Subcommand**: Introduce a new Velero server subcommand for repository maintenance.
|
||||
|
||||
2. **Create Jobs by Repository Manager**: Modify the backup repository controller to create a maintenance job instead of directly calling the multiple chain calls for Kopia or Restic maintenance.
|
||||
|
||||
3. **Update Maintenance Job Result in BackupRepository CR**: Retrieve the result of the maintenance job and update the status of the `BackupRepository` CR accordingly.
|
||||
|
||||
4. **Add Setting for Maintenance Job**: Introduce a configuration option to set maintenance jobs, including resource limits (CPU and memory), keeping the latest N maintenance jobs for each repository.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### 1. Add Maintenance sub-command
|
||||
|
||||
The CLI command will be added to the Velero CLI, the command is designed for use in a pod of maintenance jobs.
|
||||
|
||||
Our CLI command is designed as follows:
|
||||
```shell
|
||||
$ velero repo-maintenance --repo-name $repo-name --repo-type $repo-type --backup-storage-location $bsl
|
||||
```
|
||||
|
||||
Compared with other CLI commands, the maintenance command is used in a pod of maintenance jobs not for user use, and the job should show the result of maintenance after finish.
|
||||
|
||||
Here we will write the error message into one specific file which could be read by the maintenance job.
|
||||
|
||||
on the whole, we record two kinds of logs:
|
||||
|
||||
- one is the log output of the intermediate maintenance process: this log could be retrieved via the Kubernetes API server, including the error log.
|
||||
|
||||
- one is the result of the command which could indicate whether the execution is an error or not: the result could be redirected to a file that the maintenance job itself could read, and the file only contains the error message.
|
||||
|
||||
we will write the error message into the `/dev/termination-log` file if execution is failed.
|
||||
|
||||
The main maintenance logic would be using the repository provider to do the maintenance.
|
||||
|
||||
```golang
|
||||
func checkError(err error, file *os.File) {
|
||||
if err != nil {
|
||||
if err != context.Canceled {
|
||||
if _, errWrite := file.WriteString(fmt.Sprintf("An error occurred: %v", err)); errWrite != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write error to termination log file: %v\n", errWrite)
|
||||
}
|
||||
file.Close()
|
||||
os.Exit(1) // indicate the command executed failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Options) Run(f veleroCli.Factory) {
|
||||
logger := logging.DefaultLogger(o.LogLevelFlag.Parse(), o.FormatFlag.Parse())
|
||||
logger.SetOutput(os.Stdout)
|
||||
|
||||
errorFile, err := os.Create("/dev/termination-log")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to create termination log file: %v\n", err)
|
||||
return
|
||||
}
|
||||
defer errorFile.Close()
|
||||
...
|
||||
|
||||
err = o.runRepoPrune(cli, f.Namespace(), logger)
|
||||
checkError(err, errorFile)
|
||||
...
|
||||
}
|
||||
|
||||
func (o *Options) runRepoPrune(cli client.Client, namespace string, logger logrus.FieldLogger) error {
|
||||
...
|
||||
var repoProvider provider.Provider
|
||||
if o.RepoType == velerov1api.BackupRepositoryTypeRestic {
|
||||
repoProvider = provider.NewResticRepositoryProvider(credentialFileStore, filesystem.NewFileSystem(), logger)
|
||||
} else {
|
||||
repoProvider = provider.NewUnifiedRepoProvider(
|
||||
credentials.CredentialGetter{
|
||||
FromFile: credentialFileStore,
|
||||
FromSecret: credentialSecretStore,
|
||||
}, o.RepoType, cli, logger)
|
||||
}
|
||||
...
|
||||
|
||||
err = repoProvider.BoostRepoConnect(context.Background(), para)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to boost repo connect")
|
||||
}
|
||||
|
||||
err = repoProvider.PruneRepo(context.Background(), para)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to prune repo")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Create Jobs by Repository Manager
|
||||
Currently, the backup repository controller will call the repository manager to do the `PruneRepo`, and Kopia or Restic maintenance is then finally called through multiple chain calls.
|
||||
|
||||
We will keep using the `PruneRepo` function in the repository manager, but we cut off the multiple chain calls by creating a maintenance job.
|
||||
|
||||
The job definition would be like below:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
items:
|
||||
- apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
# labels or affinity or topology settings would inherit from the velero deployment
|
||||
labels:
|
||||
# label the job name for later list jobs by name
|
||||
job-name: nginx-example-default-kopia-pqz6c
|
||||
name: nginx-example-default-kopia-pqz6c
|
||||
namespace: velero
|
||||
spec:
|
||||
# Not retry it again
|
||||
backoffLimit: 1
|
||||
# Only have one job one time
|
||||
completions: 1
|
||||
# Not parallel running job
|
||||
parallelism: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
job-name: nginx-example-default-kopia-pqz6c
|
||||
name: kopia-maintenance-job
|
||||
spec:
|
||||
containers:
|
||||
# arguments for repo maintenance job
|
||||
- args:
|
||||
- repo-maintenance
|
||||
- --repo-name=nginx-example
|
||||
- --repo-type=kopia
|
||||
- --backup-storage-location=default
|
||||
# inherit from Velero server
|
||||
- --log-level=debug
|
||||
command:
|
||||
- /velero
|
||||
# inherit environment variables from the velero deployment
|
||||
env:
|
||||
- name: AZURE_CREDENTIALS_FILE
|
||||
value: /credentials/cloud
|
||||
# inherit image from the velero deployment
|
||||
image: velero/velero:main
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: kopia-maintenance-container
|
||||
# resource limitation set by Velero server configuration
|
||||
# if not specified, it would apply best effort resources allocation strategy
|
||||
resources: {}
|
||||
# error message would be written to /dev/termination-log
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
# inherit volume mounts from the velero deployment
|
||||
volumeMounts:
|
||||
- mountPath: /credentials
|
||||
name: cloud-credentials
|
||||
dnsPolicy: ClusterFirst
|
||||
restartPolicy: Never
|
||||
schedulerName: default-scheduler
|
||||
securityContext: {}
|
||||
# inherit service account from the velero deployment
|
||||
serviceAccount: velero
|
||||
serviceAccountName: velero
|
||||
volumes:
|
||||
# inherit cloud credentials from the velero deployment
|
||||
- name: cloud-credentials
|
||||
secret:
|
||||
defaultMode: 420
|
||||
secretName: cloud-credentials
|
||||
# ttlSecondsAfterFinished set the job expired seconds
|
||||
ttlSecondsAfterFinished: 86400
|
||||
status:
|
||||
# which contains the result after maintenance
|
||||
message: ""
|
||||
lastMaintenanceTime: ""
|
||||
```
|
||||
|
||||
Now, the backup repository controller will call the repository manager to create one maintenance job and wait for the job to complete. The Kopia or Restic maintenance multiple chains are called by the job.
|
||||
|
||||
### 3. Update the Result of the Maintenance Job into BackupRepository CR
|
||||
|
||||
The backup repository controller will update the result of the maintenance job into the backup repository CR.
|
||||
|
||||
For how to get the result of the maintenance job we could refer to [here](https://kubernetes.io/docs/tasks/debug/debug-application/determine-reason-pod-failure/#writing-and-reading-a-termination-message).
|
||||
|
||||
After the maintenance job is finished, we could get the result of maintenance by getting the terminated message from the related pod:
|
||||
|
||||
```golang
|
||||
func GetContainerTerminatedMessage(pod *v1.Pod) string {
|
||||
...
|
||||
for _, containerStatus := range pod.Status.ContainerStatuses {
|
||||
if containerStatus.LastTerminationState.Terminated != nil {
|
||||
return containerStatus.LastTerminationState.Terminated.Message
|
||||
}
|
||||
}
|
||||
...
|
||||
return ""
|
||||
}
|
||||
```
|
||||
Then we could update the status of backupRepository CR with the message.
|
||||
|
||||
### 4. Add Setting for Resource Usage of Maintenance
|
||||
Add one configuration for setting the resource limit of maintenance jobs as below:
|
||||
```shell
|
||||
velero server --maintenance-job-cpu-request $cpu-request --maintenance-job-mem-request $mem-request --maintenance-job-cpu-limit $cpu-limit --maintenance-job-mem-limit $mem-limit
|
||||
```
|
||||
Our default value is 0, which means we don't limit the resources, and the resource allocation strategy would be [best effort](https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/#besteffort).
|
||||
|
||||
### 5. Automatic Cleanup for Finished Maintenance Jobs
|
||||
Add configuration for clean up maintenance jobs:
|
||||
|
||||
- keep-latest-maintenance-jobs: the number of keeping latest maintenance jobs for each repository.
|
||||
|
||||
```shell
|
||||
velero server --keep-latest-maintenance-jobs $num
|
||||
```
|
||||
|
||||
We would check and keep the latest N jobs after a new job is finished.
|
||||
```golang
|
||||
func deleteOldMaintenanceJobs(cli client.Client, repo string, keep int) error {
|
||||
// Get the maintenance job list by label
|
||||
jobList := &batchv1.JobList{}
|
||||
err := cli.List(context.TODO(), jobList, client.MatchingLabels(map[string]string{RepositoryNameLabel: repo}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete old maintenance jobs
|
||||
if len(jobList.Items) > keep {
|
||||
sort.Slice(jobList.Items, func(i, j int) bool {
|
||||
return jobList.Items[i].CreationTimestamp.Before(&jobList.Items[j].CreationTimestamp)
|
||||
})
|
||||
for i := 0; i < len(jobList.Items)-keep; i++ {
|
||||
err = cli.Delete(context.TODO(), &jobList.Items[i], client.PropagationPolicy(metav1.DeletePropagationBackground))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### 6 Velero Install with Maintenance Options
|
||||
All the above maintenance options should be supported by Velero install command.
|
||||
|
||||
### 7. Observability and Debuggability
|
||||
Some monitoring metrics are added for backup repository maintenance:
|
||||
- repo_maintenance_total
|
||||
- repo_maintenance_success_total
|
||||
- repo_maintenance_failed_total
|
||||
- repo_maintenance_duration_seconds
|
||||
|
||||
We will keep the latest N maintenance jobs for each repo, and users can get the log from the job. the job log level inherent from the Velero server setting.
|
||||
|
||||
Also, we would integrate maintenance job logs and `backuprepositories` CRs into `velero debug`.
|
||||
|
||||
Roughly, the process is as follows:
|
||||
1. The backup repository controller will check the BackupRepository request in the queue periodically.
|
||||
|
||||
2. If the maintenance period of the repository checked by `runMaintenanceIfDue` in `Reconcile` is due, then the backup repository controller will call the Repository manager to execute `PruneRepo`
|
||||
|
||||
3. The `PruneRepo` of the Repository manager will create one maintenance job, the resource limitation, environment variables, service account, images, etc. would inherit from the Velero server pod. Also, one clean up TTL would be set to maintenance job.
|
||||
|
||||
4. The maintenance job will execute the Velero maintenance command, wait for maintaining to finish and write the maintenance result into the terminationMessagePath file of the related pod.
|
||||
|
||||
5. Kubernetes could show the result in the status of the pod by reading the termination message in the pod.
|
||||
|
||||
6. The backup repository controller will wait for the maintenance job to finish and read the status of the maintenance job, then update the message field and phase in the status of `backuprepositories` CR accordingly.
|
||||
|
||||
6. Clean up old maintenance jobs and keep only N latest for each repository.
|
||||
|
||||
### 8. Codes Refinement
|
||||
Once `backuprepositories` CR status is modified, the CR would re-queue to be reconciled, and re-execute logics in reconcile shortly not respecting the re-queue frequency configured by `repoSyncPeriod`.
|
||||
For one abnormal scenario if the maintenance job fails, the status of `backuprepositories` CR would be updated and the CR will re-queue immediately, if the new maintenance job still fails, then it will re-queue again, making the logic of `backuprepositories` CR re-queue like a dead loop.
|
||||
|
||||
So we change the Predicates logic in Controller manager making it only re-queue if the Spec of `backuprepositories` CR is changed.
|
||||
|
||||
```golang
|
||||
ctrl.NewControllerManagedBy(mgr).For(&velerov1api.BackupRepository{}, builder.WithPredicates(kube.SpecChangePredicate{}))
|
||||
```
|
||||
|
||||
This change would bring the behavior different from the previous, errors that occurred in the maintenance job would retry in the next reconciliation period instead of retrying immediately.
|
||||
|
||||
## Prospects for Future Work
|
||||
Future work may focus on improving the efficiency of Velero maintenance through non-blocking parallel modes. Potential areas for enhancement include:
|
||||
|
||||
**Non-blocking Mode**: Explore the implementation of a non-blocking mode for parallel maintenance to enhance overall efficiency.
|
||||
|
||||
**Concurrency Control**: Investigate mechanisms for better concurrency control of different maintenance jobs.
|
||||
|
||||
**Provider Support for Parallel Maintenance**: Evaluate the feasibility of parallel maintenance for different providers and address any compatibility issues.
|
||||
|
||||
**Efficiency Improvements**: Investigate strategies to optimize maintenance efficiency without compromising reliability.
|
||||
|
||||
By considering these areas, future iterations of Velero may benefit from enhanced parallelization and improved resource utilization during repository maintenance.
|
||||
@@ -1,120 +0,0 @@
|
||||
# Design for Adding Finalization Phase in Restore Workflow
|
||||
|
||||
## Abstract
|
||||
This design proposes adding the finalization phase to the restore workflow. The finalization phase would be entered after all item restoration and plugin operations have been completed, similar to the way the backup process proceeds. Its purpose is to perform any wrap-up work necessary before transitioning the restore process to a terminal phase.
|
||||
|
||||
## Background
|
||||
Currently, the restore process enters a terminal phase once all item restoration and plugin operations have been completed. However, there are some wrap-up works that need to be performed after item restoration and plugin operations have been fully executed. There is no suitable opportunity to perform them at present.
|
||||
|
||||
To address this, a new finalization phase should be added to the existing restore workflow. in this phase, all plugin operations and item restoration has been fully completed, which provides a clean opportunity to perform any wrap-up work before termination, improving the overall restore process.
|
||||
|
||||
Wrap-up tasks in Velero can serve several purposes:
|
||||
- Post-restore modification - Velero can modify the restored data that was temporarily changed for some purpose but required to be changed back finally or data that was newly created but missing some information. For example, [issue6435](https://github.com/vmware-tanzu/velero/issues/6435) indicates that some custom settings(like labels, reclaim policy) on restored PVs was lost because those restored PVs was newly dynamically provisioned. Velero can address it by patching the PVs' custom settings back in the finalization phase.
|
||||
- Clean up unused data - Velero can identify and delete any data that are no longer needed after a successful restore in the finalization phase.
|
||||
- Post-restore validation - Velero can validate the state of restored data and report any errors to help users locate the issue in the finalization phase.
|
||||
|
||||
The uses of wrap-up tasks are not limited to these examples. Additional needs may be addressed as they develop over time.
|
||||
|
||||
## Goals
|
||||
- Add the finalization phase and the corresponding controller to restore workflow.
|
||||
|
||||
## Non Goals
|
||||
- Implement the specific wrap-up work.
|
||||
|
||||
|
||||
## High-Level Design
|
||||
- The finalization phase will be added to current restore workflow.
|
||||
- The logic for handling current phase transition in restore and restore operations controller will be modified with the introduction of the finalization phase.
|
||||
- A new restore finalizer controller will be implemented to handle the finalization phase.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### phase transition
|
||||
Two new phases related to finalization will be added to restore workflow, which are `FinalizingPartiallyFailed` and `Finalizing`. The new phase transition will be similar to backup workflow, proceeding as follow:
|
||||
|
||||

|
||||
|
||||
### restore finalizer controller
|
||||
The new restore finalizer controller will be implemented to watch for restores in `FinalizingPartiallyFailed` and `Finalizing` phases. Any wrap-up work that needs to wait for the completion of item restoration and plugin operations will be executed by this controller, and the phase will be set to either `Completed` or `PartiallyFailed` based on the results of these works.
|
||||
|
||||
Points worth noting about the new restore finalizer controller:
|
||||
|
||||
A new structure `finalizerContext` will be created to facilitate the implementation of any wrap-up tasks. It includes all the dependencies the tasks require as well as a function `execute()` to orderly implement task logic.
|
||||
```
|
||||
// finalizerContext includes all the dependencies required by wrap-up tasks
|
||||
type finalizerContext struct {
|
||||
.......
|
||||
restore *velerov1api.Restore
|
||||
log logrus.FieldLogger
|
||||
.......
|
||||
}
|
||||
|
||||
// execute executes all the wrap-up tasks and return the result
|
||||
func (ctx *finalizerContext) execute() (results.Result, results.Result) {
|
||||
// execute task1
|
||||
.......
|
||||
|
||||
// execute task2
|
||||
.......
|
||||
|
||||
// the task execution logic will be expanded as new tasks are included
|
||||
.......
|
||||
}
|
||||
|
||||
// newFinalizerContext returns a finalizerContext object, the parameters will be added as new tasks are included.
|
||||
func newFinalizerContext(restore *velerov1api.Restore, log logrus.FieldLogger, ...) *finalizerContext{
|
||||
return &finalizerContext{
|
||||
.......
|
||||
restore: restore,
|
||||
log: log,
|
||||
.......
|
||||
}
|
||||
}
|
||||
```
|
||||
The finalizer controller is responsible for collecting all dependencies and creating a `finalizerContext` object using those dependencies. It then invokes the `execute` function.
|
||||
```
|
||||
func (r *restoreFinalizerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
.......
|
||||
|
||||
// collect all dependencies required by wrap-up tasks
|
||||
.......
|
||||
|
||||
// create a finalizerContext object and invoke execute()
|
||||
finalizerCtx := newFinalizerContext(restore, log, ...)
|
||||
warnings, errs := finalizerCtx.execute()
|
||||
|
||||
.......
|
||||
}
|
||||
|
||||
```
|
||||
After completing all necessary tasks, the result metadata in object storage will be updated if any errors or warnings occur during the execution. This behavior breaks the feature of keeping metadata files in object storage immutable, However, we believe the tradeoff is justified because it provides users with the access to examine the error/warning details when the wrap-up tasks go wrong.
|
||||
|
||||
```
|
||||
// UpdateResults updates the result metadata in object storage if necessary
|
||||
func (r *restoreFinalizerReconciler) UpdateResults(restore *api.Restore, newWarnings *results.Result, newErrs *results.Result, backupStore persistence.BackupStore) error {
|
||||
originResults, err := backupStore.GetRestoreResults(restore.Name)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error getting restore results")
|
||||
}
|
||||
warnings := originResults["warnings"]
|
||||
errs := originResults["errors"]
|
||||
warnings.Merge(newWarnings)
|
||||
errs.Merge(newErrs)
|
||||
|
||||
m := map[string]results.Result{
|
||||
"warnings": warnings,
|
||||
"errors": errs,
|
||||
}
|
||||
if err := putResults(restore, m, backupStore); err != nil {
|
||||
return errors.Wrap(err, "error putting restore results")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
The new finalization phases are added without modifying the existing phases in the restore workflow. Both new and ongoing restore processes will continue to eventually transition to a terminal phase from any prior phase, ensuring backward compatibility.
|
||||
|
||||
## Implementation
|
||||
This will be implemented during the Velero 1.14 development cycle.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 65 KiB |
@@ -29,7 +29,7 @@ During restore, the proposal is that Velero will determine if the `APIGroupVersi
|
||||
The proposed code starts with creating three lists for each backed up resource. The three lists will be created by
|
||||
(1) reading the directory names in the backup tarball file and seeing which API group versions were backed up from the source cluster,
|
||||
(2) looking at the target cluster and determining which API group versions are supported, and
|
||||
(3) getting ConfigMaps from the target cluster in order to get user-defined prioritization of versions.
|
||||
(3) getting config maps from the target cluster in order to get user-defined prioritization of versions.
|
||||
|
||||
The three lists will be used to create a map of chosen versions for each resource to restore. If there is a user-defined list of priority versions, the versions will be checked against the supported versions lists. The highest user-defined priority version that is/was supported by both target and source clusters will be the chosen version for that resource. If no user specified versions are supported by neither target nor source, the versions will be logged and the restore will continue with other prioritizations.
|
||||
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
# Schedule Skip Immediately Config Design
|
||||
## Abstract
|
||||
When unpausing schedule, a backup could be due immediately.
|
||||
New Schedules also create new backup immediately.
|
||||
|
||||
This design allows user to *skip **immediately due** backup run upon unpausing or schedule creation*.
|
||||
|
||||
## Background
|
||||
Currently, the default behavior of schedule when `.Status.LastBackup` is nil or is due immediately after unpausing, a backup will be created. This may not be a desired by all users (https://github.com/vmware-tanzu/velero/issues/6517)
|
||||
|
||||
User want ability to skip the first immediately due backup when schedule is unpaused and or created.
|
||||
|
||||
If you create a schedule with cron "45 * * * *" and pause it at say the 43rd minute and then unpause it at say 50th minute, a backup gets triggered (since .Status.LastBackup is nil or >60min ago).
|
||||
|
||||
With this design, user can skip the first immediately due backup when schedule is unpaused and or created.
|
||||
|
||||
## Goals
|
||||
- Add an option so user can when unpausing (when immediately due) or creating new schedule, to not create a backup immediately.
|
||||
|
||||
## Non Goals
|
||||
- Changing the default behavior
|
||||
|
||||
## High-Level Design
|
||||
Add a new field with to the schedule spec and as a new cli flags for install, server, schedule commands; allowing user to skip immediately due backup when unpausing or schedule creation.
|
||||
|
||||
If CLI flag is specified during schedule unpause, velero will update the schedule spec accordingly and override prior spec for `skipImmediately``.
|
||||
|
||||
## Detailed Design
|
||||
### CLI Changes
|
||||
`velero schedule unpause` will now take an optional bool flag `--skip-immediately` to allow user to override the behavior configured for velero server (see `velero server` below).
|
||||
|
||||
`velero schedule unpause schedule-1 --skip-immediately=false` will unpause the schedule but not skip the backup if due immediately from `Schedule.Status.LastBackup` timestamp. Backup will be run at the next cron schedule.
|
||||
|
||||
`velero schedule unpause schedule-1 --skip-immediately=true` will unpause the schedule and skip the backup if due immediately from `Schedule.Status.LastBackup` timestamp. Backup will also be run at the next cron schedule.
|
||||
|
||||
`velero schedule unpause schedule-1` will check `.spec.SkipImmediately` in the schedule to determine behavior. This field will default to false to maintain prior behavior.
|
||||
|
||||
`velero server` will add a new flag `--schedule-skip-immediately` to configure default value to patch new schedules created without the field. This flag will default to false to maintain prior behavior if not set.
|
||||
|
||||
`velero install` will add a new flag `--schedule-skip-immediately` to configure default value to patch new schedules created without the field. This flag will default to false to maintain prior behavior if not set.
|
||||
|
||||
### API Changes
|
||||
`pkg/apis/velero/v1/schedule_types.go`
|
||||
```diff
|
||||
// ScheduleSpec defines the specification for a Velero schedule
|
||||
type ScheduleSpec struct {
|
||||
// Template is the definition of the Backup to be run
|
||||
// on the provided schedule
|
||||
Template BackupSpec `json:"template"`
|
||||
|
||||
// Schedule is a Cron expression defining when to run
|
||||
// the Backup.
|
||||
Schedule string `json:"schedule"`
|
||||
|
||||
// UseOwnerReferencesBackup specifies whether to use
|
||||
// OwnerReferences on backups created by this Schedule.
|
||||
// +optional
|
||||
// +nullable
|
||||
UseOwnerReferencesInBackup *bool `json:"useOwnerReferencesInBackup,omitempty"`
|
||||
|
||||
// Paused specifies whether the schedule is paused or not
|
||||
// +optional
|
||||
Paused bool `json:"paused,omitempty"`
|
||||
|
||||
+ // SkipImmediately specifies whether to skip backup if schedule is due immediately from `Schedule.Status.LastBackup` timestamp when schedule is unpaused or if schedule is new.
|
||||
+ // If true, backup will be skipped immediately when schedule is unpaused if it is due based on .Status.LastBackupTimestamp or schedule is new, and will run at next schedule time.
|
||||
+ // If false, backup will not be skipped immediately when schedule is unpaused, but will run at next schedule time.
|
||||
+ // If empty, will follow server configuration (default: false).
|
||||
+ // +optional
|
||||
+ SkipImmediately bool `json:"skipImmediately,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
`LastSkipped` will be added to `ScheduleStatus` struct to track the last time a schedule was skipped.
|
||||
```diff
|
||||
// ScheduleStatus captures the current state of a Velero schedule
|
||||
type ScheduleStatus struct {
|
||||
// Phase is the current phase of the Schedule
|
||||
// +optional
|
||||
Phase SchedulePhase `json:"phase,omitempty"`
|
||||
|
||||
// LastBackup is the last time a Backup was run for this
|
||||
// Schedule schedule
|
||||
// +optional
|
||||
// +nullable
|
||||
LastBackup *metav1.Time `json:"lastBackup,omitempty"`
|
||||
|
||||
+ // LastSkipped is the last time a Schedule was skipped
|
||||
+ // +optional
|
||||
+ // +nullable
|
||||
+ LastSkipped *metav1.Time `json:"lastSkipped,omitempty"`
|
||||
|
||||
// ValidationErrors is a slice of all validation errors (if
|
||||
// applicable)
|
||||
// +optional
|
||||
ValidationErrors []string `json:"validationErrors,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
When `schedule.spec.SkipImmediately` is `true`, `LastSkipped` will be set to the current time, and `schedule.spec.SkipImmediately` set to nil so it can be used again.
|
||||
|
||||
The `getNextRunTime()` function below is updated so `LastSkipped` which is after `LastBackup` will be used to determine next run time.
|
||||
|
||||
```go
|
||||
func getNextRunTime(schedule *velerov1.Schedule, cronSchedule cron.Schedule, asOf time.Time) (bool, time.Time) {
|
||||
var lastBackupTime time.Time
|
||||
if schedule.Status.LastBackup != nil {
|
||||
lastBackupTime = schedule.Status.LastBackup.Time
|
||||
} else {
|
||||
lastBackupTime = schedule.CreationTimestamp.Time
|
||||
}
|
||||
if schedule.Status.LastSkipped != nil && schedule.Status.LastSkipped.After(lastBackupTime) {
|
||||
lastBackupTime = schedule.Status.LastSkipped.Time
|
||||
}
|
||||
|
||||
nextRunTime := cronSchedule.Next(lastBackupTime)
|
||||
|
||||
return asOf.After(nextRunTime), nextRunTime
|
||||
}
|
||||
```
|
||||
|
||||
When schedule is unpaused, and `Schedule.Status.LastBackup` is not nil, if `Schedule.Status.LastSkipped` is recent, a backup will not be created.
|
||||
|
||||
When schedule is unpaused or created with `Schedule.Status.LastBackup` set to nil or schedule is newly created, normally a backup will be created immediately. If `Schedule.Status.LastSkipped` is recent, a backup will not be created.
|
||||
|
||||
Backup will be run at the next cron schedule based on LastBackup or LastSkipped whichever is more recent.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
N/A
|
||||
|
||||
|
||||
## Security Considerations
|
||||
None
|
||||
|
||||
## Compatibility
|
||||
Upon upgrade, the new field will be added to the schedule spec automatically and will default to the prior behavior of running a backup when schedule is unpaused if it is due based on .Status.LastBackup or schedule is new.
|
||||
|
||||
Since this is a new field, it will be ignored by older versions of velero.
|
||||
|
||||
## Implementation
|
||||
TBD
|
||||
|
||||
## Open Issues
|
||||
N/A
|
||||
@@ -433,24 +433,23 @@ spec:
|
||||
volume: nginx-log
|
||||
```
|
||||
We will add the flag for both CLI installation and Helm Chart Installation. Specifically:
|
||||
- Helm Chart Installation: add the "--uploaderType" and "--default-volumes-to-fs-backup" flag into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. The changes in Value.yaml are as below:
|
||||
- Helm Chart Installation: add the "--pod-volume-backup-uploader" flag into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. The changes in Value.yaml are as below:
|
||||
```
|
||||
command:
|
||||
- /velero
|
||||
args:
|
||||
- server
|
||||
{{- with .Values.configuration }}
|
||||
- --uploader-type={{ default "restic" .uploaderType }}
|
||||
{{- if .defaultVolumesToFsBackup }}
|
||||
- --default-volumes-to-fs-backup
|
||||
{{- end }}
|
||||
{{- if .pod-volume-backup-uploader "restic" }}
|
||||
- --legacy
|
||||
{{- end }}
|
||||
```
|
||||
- CLI Installation: add the "--uploaderType" and "--default-volumes-to-fs-backup" flag into the installation command line, and then create the two deployments accordingly. Users could change the option at the time of installation. The CLI is as below:
|
||||
```velero install --uploader-type=restic --default-volumes-to-fs-backup --use-node-agent```
|
||||
```velero install --uploader-type=kopia --default-volumes-to-fs-backup --use-node-agent```
|
||||
- CLI Installation: add the "--pod-volume-backup-uploader" flag into the installation command line, and then create the two deployments accordingly. Users could change the option at the time of installation. The CLI is as below:
|
||||
```velero install --pod-volume-backup-uploader=restic```
|
||||
```velero install --pod-volume-backup-uploader=kopia```
|
||||
|
||||
## Upgrade
|
||||
For upgrade, we allow users to change the path by specifying "--uploader-type" flag in the same way as the fresh installation. Therefore, the flag change should be applied to the Velero server after upgrade. Additionally, We need to add a label to Velero server to indicate the current path, so as to provide an easy for querying it.
|
||||
For upgrade, we allow users to change the path by specifying "--pod-volume-backup-uploader" flag in the same way as the fresh installation. Therefore, the flag change should be applied to the Velero server after upgrade. Additionally, We need to add a label to Velero server to indicate the current path, so as to provide an easy for querying it.
|
||||
Moreover, if users upgrade from the old release, we need to change the existing Restic Daemonset name to VeleroNodeAgent daemonSet. The name change should be applied after upgrade.
|
||||
The recommended way for upgrade is to modify the related Velero resource directly through kubectl, the above changes will be applied in the same way. We need to modify the Velero doc for all these changes.
|
||||
|
||||
@@ -460,7 +459,7 @@ Below Velero CLI or its output needs some changes:
|
||||
- ```Velero restore describe```: the output should indicate the path
|
||||
- ```Velero restic repo get```: the name of this CLI should be changed to a generic one, for example, "Velero repo get"; the output of this CLI should print all the backup repository if Restic repository and Unified Repository exist at the same time
|
||||
|
||||
At present, we don't have a requirement for selecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is a requirement in future, we could simply add a flag similar to "--uploader-type" to select the path.
|
||||
At present, we don't have a requirement for selecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is a requirement in future, we could simply add a flag similar to "--pod-volume-backup-uploader" to select the path.
|
||||
|
||||
## CR Example
|
||||
Below sample files demonstrate complete CRs with all the changes mentioned above:
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
# Velero Uploader Configuration Integration and Extensibility
|
||||
|
||||
## Abstract
|
||||
This design proposal aims to make Velero Uploader configurable by introducing a structured approach for managing Uploader settings. we will define and standardize a data structure to facilitate future additions to Uploader configurations. This enhancement provides a template for extending Uploader-related options. And also includes examples of adding sub-options to the Uploader Configuration.
|
||||
|
||||
## Background
|
||||
Velero is widely used for backing up and restoring Kubernetes clusters. In various scenarios, optimizing the backup process is essential, future needs may arise for adding more configuration options related to the Uploader component especially when dealing with large datasets. Therefore, a standardized configuration template is required.
|
||||
|
||||
## Goals
|
||||
1. **Extensible Uploader Configuration**: Provide an extensible approach to manage Uploader configurations, making it easy to add and modify configuration options related to the Velero uploader.
|
||||
2. **User-friendliness**: Ensure that the new Uploader configuration options are easy to understand and use for Velero users without introducing excessive complexity.
|
||||
|
||||
## Non Goals
|
||||
1. Expanding to other Velero components: The primary focus of this design is Uploader configuration and does not include extending to other components or modules within Velero. Configuration changes for other components may require separate design and implementation.
|
||||
|
||||
## High-Level Design
|
||||
To achieve extensibility in Velero Uploader configurations, the following key components and changes are proposed:
|
||||
|
||||
### UploaderConfig Structure
|
||||
Two new data structures, `UploaderConfigForBackup` and `UploaderConfigForRestore`, will be defined to store Uploader configurations. These structures will include the configuration options related to backup and restore for Uploader:
|
||||
|
||||
```go
|
||||
type UploaderConfigForBackup struct {
|
||||
}
|
||||
|
||||
type UploaderConfigForRestore struct {
|
||||
}
|
||||
```
|
||||
|
||||
### Integration with Backup & Restore CRD
|
||||
The Velero CLI will support an uploader configuration-related flag, allowing users to set the value when creating backups or restores. This value will be stored in the `UploaderConfig` field within the `Backup` CRD and `Restore` CRD:
|
||||
|
||||
```go
|
||||
type BackupSpec struct {
|
||||
// UploaderConfig specifies the configuration for the uploader.
|
||||
// +optional
|
||||
// +nullable
|
||||
UploaderConfig *UploaderConfigForBackup `json:"uploaderConfig,omitempty"`
|
||||
}
|
||||
|
||||
type RestoreSpec struct {
|
||||
// UploaderConfig specifies the configuration for the restore.
|
||||
// +optional
|
||||
// +nullable
|
||||
UploaderConfig *UploaderConfigForRestore `json:"uploaderConfig,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Propagated to Different CRDs
|
||||
The configuration specified in `UploaderConfig` needs to be effective for backup and restore both by file system way and data-mover way.
|
||||
Therefore, the `UploaderConfig` field value from the `Backup` CRD should be propagated to `PodVolumeBackup` and `DataUpload` CRDs.
|
||||
|
||||
We aim for the configurations in PodVolumeBackup to originate not only from UploaderConfig in Backup but also potentially from other sources such as the server or configmap. Simultaneously, to align with the configurations in DataUpload's `DataMoverConfig map[string]string`, we have defined an `UploaderSettings map[string]string` here to record the configurations in PodVolumeBackup.
|
||||
|
||||
```go
|
||||
type PodVolumeBackupSpec struct {
|
||||
// UploaderSettings are a map of key-value pairs that should be applied to the
|
||||
// uploader configuration.
|
||||
// +optional
|
||||
// +nullable
|
||||
UploaderSettings map[string]string `json:"uploaderSettings,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
`UploaderConfig` will be stored in DataUpload's `DataMoverConfig map[string]string` field.
|
||||
|
||||
Also the `UploaderConfig` field value from the `Restore` CRD should be propagated to `PodVolumeRestore` and `DataDownload` CRDs:
|
||||
|
||||
```go
|
||||
type PodVolumeRestoreSpec struct {
|
||||
// UploaderSettings are a map of key-value pairs that should be applied to the
|
||||
// uploader configuration.
|
||||
// +optional
|
||||
// +nullable
|
||||
UploaderSettings map[string]string `json:"uploaderSettings,omitempty"`
|
||||
}
|
||||
```
|
||||
Also `UploaderConfig` will be stored in DataUpload's `DataMoverConfig map[string]string` field.
|
||||
|
||||
### Store and Get Configuration
|
||||
We need to store and retrieve configurations in the PodVolumeBackup and DataUpload structs. This involves type conversion based on the configuration type, storing it in a map[string]string, or performing type conversion from this map for retrieval.
|
||||
|
||||
PodVolumeRestore and DataDownload are also similar.
|
||||
|
||||
## Sub-options in UploaderConfig
|
||||
Adding fields above in CRDs can accommodate any future additions to Uploader configurations by adding new fields to the `UploaderConfigForBackup` or `UploaderConfigForRestore` structures.
|
||||
|
||||
### Parallel Files Upload
|
||||
This section focuses on enabling the configuration for the number of parallel file uploads during backups.
|
||||
below are the key steps that should be added to support this new feature.
|
||||
|
||||
#### Velero CLI
|
||||
The Velero CLI will support a `--parallel-files-upload` flag, allowing users to set the `ParallelFilesUpload` value when creating backups.
|
||||
|
||||
#### UploaderConfig
|
||||
below the sub-option `ParallelFilesUpload` is added into UploaderConfig:
|
||||
|
||||
```go
|
||||
// UploaderConfigForBackup defines the configuration for the uploader when doing backup.
|
||||
type UploaderConfigForBackup struct {
|
||||
// ParallelFilesUpload is the number of files parallel uploads to perform when using the uploader.
|
||||
// +optional
|
||||
ParallelFilesUpload int `json:"parallelFilesUpload,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
#### Kopia Parallel Upload Policy
|
||||
Velero Uploader can set upload policies when calling Kopia APIs. In the Kopia codebase, the structure for upload policies is defined as follows:
|
||||
|
||||
```go
|
||||
// UploadPolicy describes the policy to apply when uploading snapshots.
|
||||
type UploadPolicy struct {
|
||||
...
|
||||
MaxParallelFileReads *OptionalInt `json:"maxParallelFileReads,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
Velero can set the `MaxParallelFileReads` parameter for Kopia's upload policy as follows:
|
||||
|
||||
```go
|
||||
curPolicy := getDefaultPolicy()
|
||||
if parallelUpload > 0 {
|
||||
curPolicy.UploadPolicy.MaxParallelFileReads = newOptionalInt(parallelUpload)
|
||||
}
|
||||
```
|
||||
|
||||
#### Restic Parallel Upload Policy
|
||||
As Restic does not support parallel file upload, the configuration would not take effect, so we should output a warning when the user sets the `ParallelFilesUpload` value by using Restic to do a backup.
|
||||
|
||||
```go
|
||||
if parallelFilesUpload > 0 {
|
||||
log.Warnf("ParallelFilesUpload is set to %d, but Restic does not support parallel file uploads. Ignoring", parallelFilesUpload)
|
||||
}
|
||||
```
|
||||
|
||||
Roughly, the process is as follows:
|
||||
1. Users pass the ParallelFilesUpload parameter and its value through the Velero CLI. This parameter and its value are stored as a sub-option within UploaderConfig and then placed into the Backup CR.
|
||||
2. When users perform file system backups, UploaderConfig is passed to the PodVolumeBackup CR. When users use the Data-mover for backups, it is passed to the DataUpload CR.
|
||||
3. The configuration will be stored in map[string]string type of field in CR.
|
||||
3. Each respective controller within the CRs calls the uploader, and the ParallelFilesUpload from map in CRs is passed to the uploader.
|
||||
4. When the uploader subsequently calls the Kopia API, it can use the ParallelFilesUpload to set the MaxParallelFileReads parameter, and if the uploader calls the Restic command it would output one warning log for Restic does not support this feature.
|
||||
|
||||
### Sparse Option For Kopia & Restic Restore
|
||||
In many system files, numerous zero bytes or empty blocks persist, occupying physical storage space. Sparse restore employs a more intelligent approach, including appropriately handling empty blocks, thereby achieving the correct system state. This write sparse files mechanism aims to enhance restore efficiency while maintaining restoration accuracy.
|
||||
Below are the key steps that should be added to support this new feature.
|
||||
#### Velero CLI
|
||||
The Velero CLI will support a `--write-sparse-files` flag, allowing users to set the `WriteSparseFiles` value when creating restores with Restic or Kopia uploader.
|
||||
#### UploaderConfig
|
||||
below the sub-option `WriteSparseFiles` is added into UploaderConfig:
|
||||
```go
|
||||
// UploaderConfigForRestore defines the configuration for the restore.
|
||||
type UploaderConfigForRestore struct {
|
||||
// WriteSparseFiles is a flag to indicate whether write files sparsely or not.
|
||||
// +optional
|
||||
// +nullable
|
||||
WriteSparseFiles *bool `json:"writeSparseFiles,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
### Enable Sparse in Restic
|
||||
For Restic, it could be enabled by pass the flag `--sparse` in creating restore:
|
||||
```bash
|
||||
restic restore create --sparse $snapshotID
|
||||
```
|
||||
### Enable Sparse in Kopia
|
||||
For Kopia, it could be enabled this feature by the `WriteSparseFiles` field in the [FilesystemOutput](https://pkg.go.dev/github.com/kopia/kopia@v0.13.0/snapshot/restore#FilesystemOutput).
|
||||
|
||||
```go
|
||||
fsOutput := &restore.FilesystemOutput{
|
||||
WriteSparseFiles: uploaderutil.GetWriteSparseFiles(uploaderCfg),
|
||||
}
|
||||
```
|
||||
Roughly, the process is as follows:
|
||||
1. Users pass the WriteSparseFiles parameter and its value through the Velero CLI. This parameter and its value are stored as a sub-option within UploaderConfig and then placed into the Restore CR.
|
||||
2. When users perform file system restores, UploaderConfig is passed to the PodVolumeRestore CR. When users use the Data-mover for restores, it is passed to the DataDownload CR.
|
||||
3. The configuration will be stored in map[string]string type of field in CR.
|
||||
4. Each respective controller within the CRs calls the uploader, and the WriteSparseFiles from map in CRs is passed to the uploader.
|
||||
5. When the uploader subsequently calls the Kopia API, it can use the WriteSparseFiles to set the WriteSparseFiles parameter, and if the uploader calls the Restic command it would append `--sparse` flag within the restore command.
|
||||
|
||||
### Parallel Restore
|
||||
Setting the parallelism of restore operations can improve the efficiency and speed of the restore process, especially when dealing with large amounts of data.
|
||||
|
||||
### Velero CLI
|
||||
The Velero CLI will support a --parallel-files-download flag, allowing users to set the parallelism value when creating restores. when no value specified, the value of it would be the number of CPUs for the node that the node agent pod is running.
|
||||
```bash
|
||||
velero restore create --parallel-files-download $num
|
||||
```
|
||||
|
||||
### UploaderConfig
|
||||
below the sub-option parallel is added into UploaderConfig:
|
||||
|
||||
```go
|
||||
type UploaderConfigForRestore struct {
|
||||
// ParallelFilesDownload is the number of parallel for restore.
|
||||
// +optional
|
||||
ParallelFilesDownload int `json:"parallelFilesDownload,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
#### Kopia Parallel Restore Policy
|
||||
|
||||
Velero Uploader can set restore policies when calling Kopia APIs. In the Kopia codebase, the structure for restore policies is defined as follows:
|
||||
|
||||
```go
|
||||
// first get concurrrency from uploader config
|
||||
restoreConcurrency, _ := uploaderutil.GetRestoreConcurrency(uploaderCfg)
|
||||
// set restore concurrency into restore options
|
||||
restoreOpt := restore.Options{
|
||||
Parallel: restoreConcurrency,
|
||||
}
|
||||
// do restore with restore option
|
||||
restore.Entry(..., restoreOpt)
|
||||
```
|
||||
|
||||
#### Restic Parallel Restore Policy
|
||||
|
||||
Configurable parallel restore is not supported by restic, so we would return one error if the option is configured.
|
||||
```go
|
||||
restoreConcurrency, err := uploaderutil.GetRestoreConcurrency(uploaderCfg)
|
||||
if err != nil {
|
||||
return extraFlags, errors.Wrap(err, "failed to get uploader config")
|
||||
}
|
||||
|
||||
if restoreConcurrency > 0 {
|
||||
return extraFlags, errors.New("restic does not support parallel restore")
|
||||
}
|
||||
```
|
||||
|
||||
## Alternatives Considered
|
||||
To enhance extensibility further, the option of storing `UploaderConfig` in a Kubernetes ConfigMap can be explored, this approach would allow the addition and modification of configuration options without the need to modify the CRD.
|
||||
@@ -27,7 +27,7 @@ Moreover, we would like to create a general workflow to variations during the da
|
||||
- Support different data accesses, i.e., file system level and block level
|
||||
- Support different snapshot types, i.e., CSI snapshot, volume snapshot API from storage vendors
|
||||
- Support different snapshot accesses, i.e., through PV generated from snapshots, and through direct access API from storage vendors
|
||||
- Reuse the existing Velero generic data path as created in [Unified Repository design][1]
|
||||
- Reuse the existing Velero generic data path as creatd in [Unified Repository design][1]
|
||||
|
||||
## Non-Goals
|
||||
|
||||
@@ -84,14 +84,14 @@ Below are actions from Velero and DMP:
|
||||
**BIA Execute**
|
||||
This is the existing logic in Velero. For a source PVC/PV, Velero delivers it to the corresponding BackupItemAction plugin, the plugin then takes the related actions to back it up.
|
||||
For example, the existing CSI plugin takes a CSI snapshot to the volume represented by the PVC and then returns additional items (i.e., VolumeSnapshot, VolumeSnapshotContent and VolumeSnapshotClass) for Velero to further backup.
|
||||
To support data movement, we will use BIA V2 which supports asynchronous operation management. Here is the Execute method from BIA V2:
|
||||
To support data movement, we will use BIA V2 which supports asynchronized operation management. Here is the Execute method from BIA V2:
|
||||
```
|
||||
Execute(item runtime.Unstructured, backup *api.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, string, []velero.ResourceIdentifier, error)
|
||||
```
|
||||
Besides ```additionalItem``` (as the 2nd return value), Execute method will return one more resource list called ```itemToUpdate```, which means the items to be updated and persisted when the async operation completes. For details, visit [general progress monitoring design][2].
|
||||
Specifically, this mechanism will be used to persist DUCR into the persisted backup data, in another words, DUCR will be returned as ```itemToUpdate``` from Execute method. DUCR contains all the information the restore requires, so during restore, DUCR will be extracted from the backup data.
|
||||
Additionally, in the same way, a DMP could add any other items into the persisted backup data.
|
||||
Execute method also returns the ```operationID``` which uniquely identifies the asynchronous operation. This ```operationID``` is generated by plugins. The [general progress monitoring design][2] doesn't restrict the format of the ```operationID```, for Velero CSI plugin, the ```operationID``` is a combination of the backup CR UID and the source PVC (represented by the ```item``` parameter) UID.
|
||||
Execute method also returns the ```operationID``` which uniquely identifies the asynchronized operation. This ```operationID``` is generated by plugins. The [general progress monitoring design][2] doesn't restrict the format of the ```operationID```, for Velero CSI plugin, the ```operationID``` is a combination of the backup CR UID and the source PVC (represented by the ```item``` parameter) UID.
|
||||
|
||||
**Create Snapshot**
|
||||
The DMP creates a snapshot of the requested volume and deliver it to DM through DUCR. After that, the DMP leaves the snapshot and its related objects (e.g., VolumeSnapshot and VolumeSnapshotContent for CSI snapshot) to the DM, DM then has full control of the snapshot and its related objects, i.e., deciding whether to delete the snapshot or its related objects and when to do it.
|
||||
@@ -397,7 +397,7 @@ Target volume information includes PVC and PV that represents the volume and the
|
||||
The data mover information and backup repository information are the same with DataUpload CRD.
|
||||
DataDownload CRD defines the same status as DataUpload CRD with nearly the same meanings.
|
||||
|
||||
Below is the full spec of DataDownload CRD:
|
||||
Below is the full spec of DataUpload CRD:
|
||||
```
|
||||
apiVersion: apiextensions.k8s.io/v1alpha1
|
||||
kind: CustomResourceDefinition
|
||||
@@ -626,9 +626,10 @@ Therefore, we have below principles:
|
||||
|
||||
We will address the two principles step by step. As the first step, VBDM’s parallelism is designed as below:
|
||||
- We don’t create the load balancing mechanism for the first step, we don’t detect the accessibility of the volume/volume snapshot explicitly. Instead, we create the backupPod/restorePod under the help of Kubernetes, Kubernetes schedules the backupPod/restorePod to the appropriate node, then the data movement controller on that node will handle the DataUpload/DataDownload CR there, so the resource will be consumed from that node.
|
||||
- We expose the configurable concurrency value per node, for details of how the concurrency number constraints various backups and restores which share VGDP, check the [node-agent concurrency design][3].
|
||||
- We don’t expose the configurable concurrency value in one node, instead, the concurrency value in value will be set to 1, that is, there is no concurrency in one node.
|
||||
|
||||
As for the resource consumption, it is related to the data scale of the data movement activity and it is charged to node-agent pods, so users should configure enough resource to node-agent pods.
|
||||
Meanwhile, Pod Volume Backup/Restore are also running in node-agent pods, we don’t restrict the concurrency of these two types. For example, in one node, one Pod Volume Backup and one DataUpload could run at the same time, in this case, the resource will be shared by the two activities.
|
||||
|
||||
## Progress Report
|
||||
When a DUCR/DDCR is in InProgress phase, users could check the progress.
|
||||
@@ -665,9 +666,6 @@ At present, VBDM doesn't support recovery, so it will follow the second rule.
|
||||
## Kopia For Block Device
|
||||
To work with block devices, VGDP will be updated. Today, when Kopia attempts to create a snapshot of the block device, it will error because kopia does not support this file type. Kopia does have a nice set of interfaces that are able to be extended though.
|
||||
|
||||
**Notice**
|
||||
The Kopia block mode uploader only supports non-Windows platforms, because the block mode code invokes some system calls that are not present in the Windows platform.
|
||||
|
||||
To achieve the necessary information to determine the type of volume that is being used, we will need to pass in the volume mode in provider interface.
|
||||
|
||||
```go
|
||||
@@ -691,8 +689,7 @@ type Provider interface {
|
||||
tags map[string]string,
|
||||
forceFull bool,
|
||||
parentSnapshot string,
|
||||
volMode uploader.PersistentVolumeMode,
|
||||
uploaderCfg shared.UploaderConfig,
|
||||
volMode uploader.PersistentVolumeMode,
|
||||
updater uploader.ProgressUpdater) (string, bool, error)
|
||||
|
||||
RunRestore(
|
||||
@@ -930,20 +927,12 @@ For 3, Velero leverage on DMs to decide how to save the log, but they will not g
|
||||
## Installation
|
||||
DMs need to be configured during installation so that they can be installed. Plugin DMs may have their own configuration, for VGDM, the only requirement is to install Velero node-agent.
|
||||
Moreover, the DMP is also required during the installation.
|
||||
|
||||
From release-1.14, the `github.com/vmware-tanzu/velero-plugin-for-csi` repository, which is the Velero CSI plugin, is merged into the `github.com/vmware-tanzu/velero` repository.
|
||||
The reason to merge the CSI plugin is:
|
||||
* The VolumeSnapshot data mover depends on the CSI plugin, it's reasonabe to integrate them.
|
||||
* This change reduces the Velero deploying complexity.
|
||||
* This makes performance tuning easier in the future.
|
||||
|
||||
As a result, no need to install Velero CSI plugin anymore.
|
||||
|
||||
For example, to move CSI snapshot through VBDM, below is the installation script:
|
||||
```
|
||||
velero install \
|
||||
--provider \
|
||||
--image \
|
||||
--plugins velero/velero-plugin-for-csi:xxx \
|
||||
--features=EnableCSI \
|
||||
--use-node-agent \
|
||||
```
|
||||
@@ -977,6 +966,5 @@ Restore command is kept as is.
|
||||
|
||||
|
||||
|
||||
[1]: ../Implemented/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md
|
||||
[2]: ../Implemented/general-progress-monitoring.md
|
||||
[3]: ../node-agent-concurrency.md
|
||||
[1]: ../unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md
|
||||
[2]: ../general-progress-monitoring.md
|
||||
214
go.mod
214
go.mod
@@ -1,178 +1,162 @@
|
||||
module github.com/vmware-tanzu/velero
|
||||
|
||||
go 1.22.6
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.40.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.6.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.3
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.14
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.11
|
||||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.143.0
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7
|
||||
cloud.google.com/go/storage v1.30.1
|
||||
github.com/Azure/azure-pipeline-go v0.2.3
|
||||
github.com/Azure/azure-sdk-for-go v67.2.0+incompatible
|
||||
github.com/Azure/azure-storage-blob-go v0.15.0
|
||||
github.com/Azure/go-autorest/autorest v0.11.27
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0
|
||||
github.com/aws/aws-sdk-go v1.44.253
|
||||
github.com/bombsimon/logrusr/v3 v3.0.0
|
||||
github.com/evanphx/json-patch/v5 v5.8.0
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/golang/protobuf v1.5.3
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/hashicorp/go-hclog v0.14.1
|
||||
github.com/hashicorp/go-plugin v1.6.0
|
||||
github.com/hashicorp/go-plugin v1.4.3
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/kopia/kopia v0.16.0
|
||||
github.com/kubernetes-csi/external-snapshotter/client/v7 v7.0.0
|
||||
github.com/kopia/kopia v0.13.0
|
||||
github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/gomega v1.30.0
|
||||
github.com/onsi/gomega v1.20.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.19.0
|
||||
github.com/prometheus/client_golang v1.15.0
|
||||
github.com/robfig/cron v1.1.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/afero v1.6.0
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spf13/cobra v1.4.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/vmware-tanzu/crash-diagnostics v0.3.7
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
||||
golang.org/x/mod v0.17.0
|
||||
golang.org/x/net v0.26.0
|
||||
golang.org/x/oauth2 v0.19.0
|
||||
golang.org/x/text v0.16.0
|
||||
google.golang.org/api v0.172.0
|
||||
google.golang.org/grpc v1.63.2
|
||||
google.golang.org/protobuf v1.33.0
|
||||
go.uber.org/zap v1.24.0
|
||||
golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f
|
||||
golang.org/x/mod v0.10.0
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/oauth2 v0.7.0
|
||||
golang.org/x/text v0.14.0
|
||||
google.golang.org/api v0.120.0
|
||||
google.golang.org/grpc v1.56.3
|
||||
google.golang.org/protobuf v1.30.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.29.0
|
||||
k8s.io/apiextensions-apiserver v0.29.0
|
||||
k8s.io/apimachinery v0.29.0
|
||||
k8s.io/api v0.25.6
|
||||
k8s.io/apiextensions-apiserver v0.24.2
|
||||
k8s.io/apimachinery v0.25.6
|
||||
k8s.io/cli-runtime v0.24.0
|
||||
k8s.io/client-go v0.29.0
|
||||
k8s.io/klog/v2 v2.110.1
|
||||
k8s.io/client-go v0.25.6
|
||||
k8s.io/klog/v2 v2.70.1
|
||||
k8s.io/kube-aggregator v0.19.12
|
||||
k8s.io/metrics v0.25.6
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
|
||||
sigs.k8s.io/controller-runtime v0.17.2
|
||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed
|
||||
sigs.k8s.io/controller-runtime v0.12.2
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.112.1 // indirect
|
||||
cloud.google.com/go/compute v1.24.0 // indirect
|
||||
cloud.google.com/go v0.110.0 // indirect
|
||||
cloud.google.com/go/compute v1.19.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v1.1.7 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 // indirect
|
||||
github.com/aws/smithy-go v1.19.0 // indirect
|
||||
cloud.google.com/go/iam v0.13.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/validation v0.2.0 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chmduquesne/rollinghash v4.0.0+incompatible // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-logr/zapr v1.3.0 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-logr/zapr v1.2.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/swag v0.21.1 // indirect
|
||||
github.com/gofrs/flock v0.8.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/gnostic v0.6.9 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/hashicorp/cronexpr v1.1.2 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/google/s2a-go v0.1.2 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.8.0 // indirect
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.8 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/klauspost/reedsolomon v1.12.1 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.16.5 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/klauspost/reedsolomon v1.11.7 // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-ieproxy v0.0.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.69 // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.52 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
|
||||
github.com/moby/spdystream v0.2.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/mxk/go-vss v1.2.0 // indirect
|
||||
github.com/natefinch/atomic v1.0.1 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/oklog/run v1.0.0 // indirect
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.52.3 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.42.0 // indirect
|
||||
github.com/prometheus/procfs v0.9.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/rs/xid v1.4.0 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/vladimirvivien/gexe v0.1.1 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel v1.14.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.14.0 // indirect
|
||||
go.starlark.net v0.0.0-20201006213952-227f4aabceb5 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/term v0.21.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/term v0.15.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/component-base v0.29.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
k8s.io/component-base v0.24.2 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
)
|
||||
|
||||
replace github.com/kopia/kopia => github.com/project-velero/kopia v0.0.0-20240417031915-e07d5b7de567
|
||||
replace github.com/kopia/kopia => github.com/project-velero/kopia v0.13.0-velero.1
|
||||
|
||||
@@ -12,11 +12,31 @@ run:
|
||||
# exit code when at least one issue was found, default is 1
|
||||
issues-exit-code: 1
|
||||
|
||||
# which dirs to skip: issues from them won't be reported;
|
||||
# can use regexp here: generated.*, regexp is applied on full path;
|
||||
# default value is empty list, but default dirs are skipped independently
|
||||
# from this option's value (see skip-dirs-use-default).
|
||||
# "/" will be replaced by current OS file path separator to properly work
|
||||
# on Windows.
|
||||
skip-dirs:
|
||||
- test/*
|
||||
- pkg/plugin/generated/*
|
||||
# - autogenerated_by_my_lib
|
||||
|
||||
# default is true. Enables skipping of directories:
|
||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
skip-dirs-use-default: true
|
||||
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
# "/" will be replaced by current OS file path separator to properly work
|
||||
# on Windows.
|
||||
skip-files:
|
||||
- ".*_test.go$"
|
||||
# - lib/bad.go
|
||||
|
||||
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
|
||||
# If invoked with -mod=readonly, the go command is disallowed from the implicit
|
||||
# automatic updating of go.mod described above. Instead, it fails when any changes
|
||||
@@ -36,9 +56,7 @@ run:
|
||||
# output configuration options
|
||||
output:
|
||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
||||
formats:
|
||||
- format: colored-line-number
|
||||
path: stdout
|
||||
format: colored-line-number
|
||||
|
||||
# print lines of code with issue, default is true
|
||||
print-issued-lines: true
|
||||
@@ -142,8 +160,10 @@ linters-settings:
|
||||
# minimal confidence for issues, default is 0.8
|
||||
min-confidence: 0.8
|
||||
gomnd:
|
||||
# the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.
|
||||
checks: argument,case,condition,operation,return,assign
|
||||
settings:
|
||||
mnd:
|
||||
# the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.
|
||||
checks: argument,case,condition,operation,return,assign
|
||||
gomodguard:
|
||||
allowed:
|
||||
modules: # List of allowed modules
|
||||
@@ -235,24 +255,6 @@ linters-settings:
|
||||
rowserrcheck:
|
||||
packages:
|
||||
- github.com/jmoiron/sqlx
|
||||
testifylint:
|
||||
# TODO: enable them all
|
||||
disable:
|
||||
- bool-compare
|
||||
- compares
|
||||
- error-is-as
|
||||
- error-nil
|
||||
- expected-actual
|
||||
- go-require
|
||||
- float-compare
|
||||
- require-error
|
||||
- suite-dont-use-pkg
|
||||
- suite-extra-assert-call
|
||||
- suite-thelper
|
||||
enable:
|
||||
- empty
|
||||
- len
|
||||
- nil-compare
|
||||
testpackage:
|
||||
# regexp pattern to skip files
|
||||
skip-regexp: (export|internal)_test\.go
|
||||
@@ -300,10 +302,8 @@ linters:
|
||||
- bodyclose
|
||||
- dogsled
|
||||
- durationcheck
|
||||
- dupword
|
||||
- errcheck
|
||||
- exportloopref
|
||||
- errchkjson
|
||||
- goconst
|
||||
- gofmt
|
||||
- goheader
|
||||
@@ -312,19 +312,14 @@ linters:
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ginkgolinter
|
||||
- importas
|
||||
- ineffassign
|
||||
- misspell
|
||||
- nakedret
|
||||
- nosprintfhostport
|
||||
- nilerr
|
||||
- noctx
|
||||
- nolintlint
|
||||
- revive
|
||||
- staticcheck
|
||||
- stylecheck
|
||||
- testifylint
|
||||
- revive
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
@@ -339,34 +334,12 @@ issues:
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "github.com/golang/protobuf/proto" # grpc-go still uses github.com/golang/protobuf/proto.
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage" # Kopia still depends on this.
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "DefaultVolumesToRestic" # No need to report deprecate for DefaultVolumesToRestic.
|
||||
- path: ".*_test.go$"
|
||||
linters:
|
||||
- dupword
|
||||
- errcheck
|
||||
- goconst
|
||||
- gosec
|
||||
- govet
|
||||
- staticcheck
|
||||
- stylecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- path: test/
|
||||
linters:
|
||||
- dupword
|
||||
- errcheck
|
||||
- goconst
|
||||
- gosec
|
||||
- gosimple
|
||||
- nilerr
|
||||
- staticcheck
|
||||
- stylecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
|
||||
# The list of ids of default excludes to include or disable. By default it's empty.
|
||||
include:
|
||||
@@ -381,15 +354,6 @@ issues:
|
||||
# Show only new issues created after git revision `REV`
|
||||
# new-from-rev: origin/main
|
||||
|
||||
# which dirs to skip: issues from them won't be reported;
|
||||
# can use regexp here: generated.*, regexp is applied on full path;
|
||||
# default value is empty list, but default dirs are skipped independently
|
||||
# from this option's value (see skip-dirs-use-default).
|
||||
# "/" will be replaced by current OS file path separator to properly work
|
||||
# on Windows.
|
||||
exclude-dirs:
|
||||
- pkg/plugin/generated/*
|
||||
|
||||
severity:
|
||||
# Default value is empty string.
|
||||
# Set the default severity for issues. If severity rules are defined and the issues
|
||||
@@ -413,4 +377,4 @@ severity:
|
||||
rules:
|
||||
- linters:
|
||||
- dupl
|
||||
severity: info
|
||||
severity: info
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM --platform=$TARGETPLATFORM golang:1.22.6-bookworm
|
||||
FROM --platform=linux/amd64 golang:1.20.10-bullseye
|
||||
|
||||
ARG GOPROXY
|
||||
|
||||
@@ -21,16 +21,16 @@ ENV GO111MODULE=on
|
||||
ENV GOPROXY=${GOPROXY}
|
||||
|
||||
# kubebuilder test bundle is separated from kubebuilder. Need to setup it for CI test.
|
||||
RUN curl -sSLo envtest-bins.tar.gz https://go.kubebuilder.io/test-tools/1.22.1/linux/$(go env GOARCH) && \
|
||||
RUN curl -sSLo envtest-bins.tar.gz https://go.kubebuilder.io/test-tools/1.22.1/linux/amd64 && \
|
||||
mkdir /usr/local/kubebuilder && \
|
||||
tar -C /usr/local/kubebuilder --strip-components=1 -zvxf envtest-bins.tar.gz
|
||||
|
||||
RUN wget --quiet https://github.com/kubernetes-sigs/kubebuilder/releases/download/v3.2.0/kubebuilder_linux_$(go env GOARCH) && \
|
||||
mv kubebuilder_linux_$(go env GOARCH) /usr/local/kubebuilder/bin/kubebuilder && \
|
||||
RUN wget --quiet https://github.com/kubernetes-sigs/kubebuilder/releases/download/v3.2.0/kubebuilder_linux_amd64 && \
|
||||
mv kubebuilder_linux_amd64 /usr/local/kubebuilder/bin/kubebuilder && \
|
||||
chmod +x /usr/local/kubebuilder/bin/kubebuilder
|
||||
|
||||
# get controller-tools
|
||||
RUN go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.14.0
|
||||
RUN go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.12.0
|
||||
|
||||
# get goimports (the revision is pinned so we don't indiscriminately update, but the particular commit
|
||||
# is not important)
|
||||
@@ -39,66 +39,26 @@ RUN go install golang.org/x/tools/cmd/goimports@11e9d9cc0042e6bd10337d4d2c3e5d92
|
||||
# get protoc compiler and golang plugin
|
||||
WORKDIR /root
|
||||
RUN apt-get update && apt-get install -y unzip
|
||||
# protobuf uses bazel cpunames except following
|
||||
# if cpu == "systemz":
|
||||
# cpu = "s390_64"
|
||||
# elif cpu == "aarch64":
|
||||
# cpu = "aarch_64"
|
||||
# elif cpu == "ppc64":
|
||||
# cpu = "ppcle_64"
|
||||
# snippet from: https://github.com/protocolbuffers/protobuf/blob/d445953603e66eb8992a39b4e10fcafec8501f24/protobuf_release.bzl#L18-L24
|
||||
# cpu names: https://github.com/bazelbuild/platforms/blob/main/cpu/BUILD
|
||||
RUN ARCH=$(go env GOARCH) && \
|
||||
if [ "$ARCH" = "s390x" ] ; then \
|
||||
ARCH="s390_64"; \
|
||||
elif [ "$ARCH" = "arm64" ] ; then \
|
||||
ARCH="aarch_64"; \
|
||||
elif [ "$ARCH" = "ppc64le" ] ; then \
|
||||
ARCH="ppcle_64"; \
|
||||
elif [ "$ARCH" = "ppc64" ] ; then \
|
||||
ARCH="ppcle_64"; \
|
||||
else \
|
||||
ARCH=$(uname -m); \
|
||||
fi && echo "ARCH=$ARCH" && \
|
||||
wget --quiet https://github.com/protocolbuffers/protobuf/releases/download/v25.2/protoc-25.2-linux-$ARCH.zip && \
|
||||
unzip protoc-25.2-linux-$ARCH.zip; \
|
||||
rm *.zip && \
|
||||
RUN wget --quiet https://github.com/protocolbuffers/protobuf/releases/download/v3.14.0/protoc-3.14.0-linux-x86_64.zip && \
|
||||
unzip protoc-3.14.0-linux-x86_64.zip && \
|
||||
mv bin/protoc /usr/bin/protoc && \
|
||||
mv include/google /usr/include && \
|
||||
chmod a+x /usr/include/google && \
|
||||
chmod a+x /usr/include/google/protobuf && \
|
||||
chmod a+x /usr/include/google && \
|
||||
chmod a+x /usr/include/google/protobuf && \
|
||||
chmod a+r -R /usr/include/google && \
|
||||
chmod +x /usr/bin/protoc
|
||||
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.32.0
|
||||
RUN go install github.com/golang/protobuf/protoc-gen-go@v1.4.3
|
||||
|
||||
# get goreleaser
|
||||
# goreleaser name template per arch is basically goarch except for amd64 and 386 https://github.com/goreleaser/goreleaser/blob/ec8819a95c5527fae65e5cb41673f5bbc3245fda/.goreleaser.yaml#L167C1-L173C42
|
||||
# {{- .ProjectName }}_
|
||||
# {{- title .Os }}_
|
||||
# {{- if eq .Arch "amd64" }}x86_64
|
||||
# {{- else if eq .Arch "386" }}i386
|
||||
# {{- else }}{{ .Arch }}{{ end }}
|
||||
# {{- if .Arm }}v{{ .Arm }}{{ end -}}
|
||||
RUN ARCH=$(go env GOARCH) && \
|
||||
if [ "$ARCH" = "amd64" ] ; then \
|
||||
ARCH="x86_64"; \
|
||||
elif [ "$ARCH" = "386" ] ; then \
|
||||
ARCH="i386"; \
|
||||
elif [ "$ARCH" = "ppc64le" ] ; then \
|
||||
ARCH="ppc64"; \
|
||||
fi && \
|
||||
wget --quiet "https://github.com/goreleaser/goreleaser/releases/download/v1.15.2/goreleaser_Linux_$ARCH.tar.gz" && \
|
||||
tar xvf goreleaser_Linux_$ARCH.tar.gz; \
|
||||
RUN wget --quiet https://github.com/goreleaser/goreleaser/releases/download/v1.15.2/goreleaser_Linux_x86_64.tar.gz && \
|
||||
tar xvf goreleaser_Linux_x86_64.tar.gz && \
|
||||
mv goreleaser /usr/bin/goreleaser && \
|
||||
chmod +x /usr/bin/goreleaser
|
||||
|
||||
# get golangci-lint
|
||||
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.57.2
|
||||
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.51.0
|
||||
|
||||
# install kubectl
|
||||
RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/$(go env GOARCH)/kubectl
|
||||
RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
|
||||
RUN chmod +x ./kubectl
|
||||
RUN mv ./kubectl /usr/local/bin
|
||||
|
||||
# Fix the "dubious ownership" issue from git when running goreleaser.sh
|
||||
RUN echo "[safe] \n\t directory = *" > /.gitconfig
|
||||
@@ -32,10 +32,6 @@ if [[ -z "${GOOS}" ]]; then
|
||||
echo "GOOS must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${GOBIN}" ]]; then
|
||||
echo "GOBIN must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${GOARCH}" ]]; then
|
||||
echo "GOARCH must be set"
|
||||
exit 1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/go.mod b/go.mod
|
||||
index 5f939c481..1caa51275 100644
|
||||
index 5f939c481..a2c584c4d 100644
|
||||
--- a/go.mod
|
||||
+++ b/go.mod
|
||||
@@ -24,32 +24,32 @@ require (
|
||||
@@ -9,16 +9,16 @@ index 5f939c481..1caa51275 100644
|
||||
- golang.org/x/crypto v0.5.0
|
||||
- golang.org/x/net v0.5.0
|
||||
- golang.org/x/oauth2 v0.4.0
|
||||
+ golang.org/x/crypto v0.21.0
|
||||
+ golang.org/x/net v0.23.0
|
||||
+ golang.org/x/crypto v0.17.0
|
||||
+ golang.org/x/net v0.17.0
|
||||
+ golang.org/x/oauth2 v0.7.0
|
||||
golang.org/x/sync v0.1.0
|
||||
- golang.org/x/sys v0.4.0
|
||||
- golang.org/x/term v0.4.0
|
||||
- golang.org/x/text v0.6.0
|
||||
- google.golang.org/api v0.106.0
|
||||
+ golang.org/x/sys v0.18.0
|
||||
+ golang.org/x/term v0.18.0
|
||||
+ golang.org/x/sys v0.15.0
|
||||
+ golang.org/x/term v0.15.0
|
||||
+ golang.org/x/text v0.14.0
|
||||
+ google.golang.org/api v0.114.0
|
||||
)
|
||||
@@ -57,12 +57,12 @@ index 5f939c481..1caa51275 100644
|
||||
- google.golang.org/protobuf v1.28.1 // indirect
|
||||
+ google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
+ google.golang.org/grpc v1.56.3 // indirect
|
||||
+ google.golang.org/protobuf v1.33.0 // indirect
|
||||
+ google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
diff --git a/go.sum b/go.sum
|
||||
index 026e1d2fa..27d4207f4 100644
|
||||
index 026e1d2fa..8387b4e2f 100644
|
||||
--- a/go.sum
|
||||
+++ b/go.sum
|
||||
@@ -1,13 +1,13 @@
|
||||
@@ -126,8 +126,8 @@ index 026e1d2fa..27d4207f4 100644
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
-golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
||||
-golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
+golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
+golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
+golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
+golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@@ -137,8 +137,8 @@ index 026e1d2fa..27d4207f4 100644
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
-golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
||||
-golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
+golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
+golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
+golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
+golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
-golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M=
|
||||
-golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
|
||||
@@ -153,13 +153,13 @@ index 026e1d2fa..27d4207f4 100644
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
-golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
-golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
+golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
+golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
+golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
+golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
-golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
|
||||
-golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
+golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
+golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
+golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
+golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -208,8 +208,8 @@ index 026e1d2fa..27d4207f4 100644
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
-google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
-google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
HACK_DIR=$(dirname "${BASH_SOURCE[0]}")
|
||||
|
||||
# Printing out cache status
|
||||
golangci-lint cache status
|
||||
|
||||
# Enable GL_DEBUG line below for debug messages for golangci-lint
|
||||
# export GL_DEBUG=loader,gocritic,env
|
||||
CMD="golangci-lint run"
|
||||
CMD="golangci-lint run -c $HACK_DIR/../golangci.yaml"
|
||||
echo "Running $CMD"
|
||||
|
||||
eval $CMD
|
||||
|
||||
@@ -21,9 +21,10 @@ set -o pipefail
|
||||
|
||||
export CGO_ENABLED=0
|
||||
|
||||
TARGETS=($(go list ./pkg/... ./internal/...| grep -vE "/pkg/builder|pkg/apis|pkg/test|pkg/generated|pkg/plugin/generated|mocks|internal/restartabletest"))
|
||||
TARGETS=($(go list ./pkg/... | grep -v "github.com/vmware-tanzu/velero/pkg/builder"))
|
||||
TARGETS+=(
|
||||
./cmd/...
|
||||
./internal/...
|
||||
)
|
||||
|
||||
if [[ ${#@} -ne 0 ]]; then
|
||||
|
||||
@@ -33,7 +33,7 @@ if ! command -v goimports > /dev/null; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
files="$(find . -type f -name '*.go' -not -path './.go/*' -not -path './vendor/*' -not -path './site/*' -not -path './.git/*' -not -path '*/generated/*' -not -name 'zz_generated*' -not -path '*/mocks/*')"
|
||||
files="$(find . -type f -name '*.go' -not -path './.go/*' -not -path './vendor/*' -not -path './site/*' -not -path '*/generated/*' -not -name 'zz_generated*' -not -path '*/mocks/*')"
|
||||
echo "${ACTION} gofmt"
|
||||
output=$(gofmt "${MODE}" -s ${files})
|
||||
if [[ -n "${output}" ]]; then
|
||||
|
||||
@@ -19,7 +19,7 @@ HACK_DIR=$(dirname "${BASH_SOURCE}")
|
||||
${HACK_DIR}/update-3generated-crd-code.sh
|
||||
|
||||
# ensure no changes to generated CRDs
|
||||
if ! git diff --exit-code config/crd/v1/crds/crds.go config/crd/v2alpha1/crds/crds.go &> /dev/null; then
|
||||
if [! git diff --exit-code config/crd/v1/crds/crds.go config/crd/v2alpha1/crds/crds.go >/dev/null]; then
|
||||
# revert changes to state before running CRD generation to stay consistent
|
||||
# with code-generator `--verify-only` option which discards generated changes
|
||||
git checkout config/crd
|
||||
|
||||
@@ -1,120 +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 csi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v7/apis/volumesnapshot/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
crclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/client"
|
||||
plugincommon "github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/csi"
|
||||
kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
)
|
||||
|
||||
// volumeSnapshotDeleteItemAction is a backup item action plugin for Velero.
|
||||
type volumeSnapshotDeleteItemAction struct {
|
||||
log logrus.FieldLogger
|
||||
crClient crclient.Client
|
||||
}
|
||||
|
||||
// AppliesTo returns information indicating that the
|
||||
// VolumeSnapshotBackupItemAction should be invoked to backup
|
||||
// VolumeSnapshots.
|
||||
func (p *volumeSnapshotDeleteItemAction) AppliesTo() (velero.ResourceSelector, error) {
|
||||
p.log.Debug("VolumeSnapshotBackupItemAction AppliesTo")
|
||||
|
||||
return velero.ResourceSelector{
|
||||
IncludedResources: []string{"volumesnapshots.snapshot.storage.k8s.io"},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *volumeSnapshotDeleteItemAction) Execute(
|
||||
input *velero.DeleteItemActionExecuteInput,
|
||||
) error {
|
||||
p.log.Info("Starting VolumeSnapshotDeleteItemAction for volumeSnapshot")
|
||||
|
||||
var vs snapshotv1api.VolumeSnapshot
|
||||
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(
|
||||
input.Item.UnstructuredContent(),
|
||||
&vs,
|
||||
); err != nil {
|
||||
return errors.Wrapf(err, "failed to convert input.Item from unstructured")
|
||||
}
|
||||
|
||||
// We don't want this DeleteItemAction plugin to delete VolumeSnapshot
|
||||
// taken outside of Velero. So skip deleting VolumeSnapshot objects
|
||||
// that were not created in the process of creating the Velero
|
||||
// backup being deleted.
|
||||
if !kubeutil.HasBackupLabel(&vs.ObjectMeta, input.Backup.Name) {
|
||||
p.log.Info(
|
||||
"VolumeSnapshot %s/%s was not taken by backup %s, skipping deletion",
|
||||
vs.Namespace, vs.Name, input.Backup.Name,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
p.log.Infof("Deleting VolumeSnapshot %s/%s", vs.Namespace, vs.Name)
|
||||
if vs.Status != nil && vs.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 := csi.SetVolumeSnapshotContentDeletionPolicy(
|
||||
*vs.Status.BoundVolumeSnapshotContentName,
|
||||
p.crClient,
|
||||
)
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
return errors.Wrapf(
|
||||
err,
|
||||
fmt.Sprintf("failed to patch DeletionPolicy of volume snapshot %s/%s",
|
||||
vs.Namespace, vs.Name),
|
||||
)
|
||||
}
|
||||
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err := p.crClient.Delete(context.TODO(), &vs)
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewVolumeSnapshotDeleteItemAction(f client.Factory) plugincommon.HandlerInitializer {
|
||||
return func(logger logrus.FieldLogger) (interface{}, error) {
|
||||
crClient, err := f.KubebuilderClient()
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return &volumeSnapshotDeleteItemAction{
|
||||
log: logger,
|
||||
crClient: crClient,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
@@ -1,151 +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 csi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v7/apis/volumesnapshot/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
)
|
||||
|
||||
func TestVSExecute(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
item runtime.Unstructured
|
||||
vs *snapshotv1api.VolumeSnapshot
|
||||
backup *velerov1api.Backup
|
||||
createVS bool
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "VolumeSnapshot doesn't have backup label",
|
||||
item: velerotest.UnstructuredOrDie(
|
||||
`
|
||||
{
|
||||
"apiVersion": "snapshot.storage.k8s.io/v1",
|
||||
"kind": "VolumeSnapshot",
|
||||
"metadata": {
|
||||
"namespace": "ns",
|
||||
"name": "foo"
|
||||
}
|
||||
}
|
||||
`,
|
||||
),
|
||||
backup: builder.ForBackup("velero", "backup").Result(),
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "VolumeSnapshot doesn't exist in the cluster",
|
||||
vs: builder.ForVolumeSnapshot("foo", "bar").
|
||||
ObjectMeta(builder.WithLabelsMap(
|
||||
map[string]string{velerov1api.BackupNameLabel: "backup"},
|
||||
)).Status().
|
||||
BoundVolumeSnapshotContentName("vsc").
|
||||
Result(),
|
||||
backup: builder.ForBackup("velero", "backup").Result(),
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "Normal case, VolumeSnapshot should be deleted",
|
||||
vs: builder.ForVolumeSnapshot("foo", "bar").
|
||||
ObjectMeta(builder.WithLabelsMap(
|
||||
map[string]string{velerov1api.BackupNameLabel: "backup"},
|
||||
)).Status().
|
||||
BoundVolumeSnapshotContentName("vsc").
|
||||
Result(),
|
||||
backup: builder.ForBackup("velero", "backup").Result(),
|
||||
expectErr: false,
|
||||
createVS: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
crClient := velerotest.NewFakeControllerRuntimeClient(t)
|
||||
logger := logrus.StandardLogger()
|
||||
|
||||
p := volumeSnapshotDeleteItemAction{log: logger, crClient: crClient}
|
||||
|
||||
if test.vs != nil {
|
||||
vsMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(test.vs)
|
||||
require.NoError(t, err)
|
||||
test.item = &unstructured.Unstructured{Object: vsMap}
|
||||
}
|
||||
|
||||
if test.createVS {
|
||||
require.NoError(t, crClient.Create(context.TODO(), test.vs))
|
||||
}
|
||||
|
||||
err := p.Execute(
|
||||
&velero.DeleteItemActionExecuteInput{
|
||||
Item: test.item,
|
||||
Backup: test.backup,
|
||||
},
|
||||
)
|
||||
|
||||
if test.expectErr == false {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVSAppliesTo(t *testing.T) {
|
||||
p := volumeSnapshotDeleteItemAction{
|
||||
log: logrus.StandardLogger(),
|
||||
}
|
||||
selector, err := p.AppliesTo()
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(
|
||||
t,
|
||||
velero.ResourceSelector{
|
||||
IncludedResources: []string{"volumesnapshots.snapshot.storage.k8s.io"},
|
||||
},
|
||||
selector,
|
||||
)
|
||||
}
|
||||
|
||||
func TestNewVolumeSnapshotDeleteItemAction(t *testing.T) {
|
||||
logger := logrus.StandardLogger()
|
||||
crClient := velerotest.NewFakeControllerRuntimeClient(t)
|
||||
|
||||
f := &factorymocks.Factory{}
|
||||
f.On("KubebuilderClient").Return(nil, fmt.Errorf(""))
|
||||
plugin := NewVolumeSnapshotDeleteItemAction(f)
|
||||
_, err := plugin(logger)
|
||||
require.Error(t, err)
|
||||
|
||||
f1 := &factorymocks.Factory{}
|
||||
f1.On("KubebuilderClient").Return(crClient, nil)
|
||||
plugin1 := NewVolumeSnapshotDeleteItemAction(f1)
|
||||
_, err1 := plugin1(logger)
|
||||
require.NoError(t, err1)
|
||||
}
|
||||
@@ -1,126 +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 csi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v7/apis/volumesnapshot/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
crclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/client"
|
||||
plugincommon "github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/csi"
|
||||
kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
)
|
||||
|
||||
// volumeSnapshotContentDeleteItemAction is a restore item action plugin for Velero
|
||||
type volumeSnapshotContentDeleteItemAction struct {
|
||||
log logrus.FieldLogger
|
||||
crClient crclient.Client
|
||||
}
|
||||
|
||||
// AppliesTo returns information indicating
|
||||
// VolumeSnapshotContentRestoreItemAction action should be invoked
|
||||
// while restoring VolumeSnapshotContent.snapshot.storage.k8s.io resources
|
||||
func (p *volumeSnapshotContentDeleteItemAction) AppliesTo() (velero.ResourceSelector, error) {
|
||||
return velero.ResourceSelector{
|
||||
IncludedResources: []string{"volumesnapshotcontents.snapshot.storage.k8s.io"},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *volumeSnapshotContentDeleteItemAction) Execute(
|
||||
input *velero.DeleteItemActionExecuteInput,
|
||||
) error {
|
||||
p.log.Info("Starting VolumeSnapshotContentDeleteItemAction")
|
||||
|
||||
var snapCont snapshotv1api.VolumeSnapshotContent
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(
|
||||
input.Item.UnstructuredContent(),
|
||||
&snapCont,
|
||||
); err != nil {
|
||||
return errors.Wrapf(err, "failed to convert VolumeSnapshotContent from unstructured")
|
||||
}
|
||||
|
||||
// We don't want this DeleteItemAction plugin to delete
|
||||
// VolumeSnapshotContent taken outside of Velero.
|
||||
// So skip deleting VolumeSnapshotContent not have the backup name
|
||||
// in its labels.
|
||||
if !kubeutil.HasBackupLabel(&snapCont.ObjectMeta, input.Backup.Name) {
|
||||
p.log.Info(
|
||||
"VolumeSnapshotContent %s was not taken by backup %s, skipping deletion",
|
||||
snapCont.Name,
|
||||
input.Backup.Name,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
p.log.Infof("Deleting VolumeSnapshotContent %s", snapCont.Name)
|
||||
|
||||
if err := csi.SetVolumeSnapshotContentDeletionPolicy(
|
||||
snapCont.Name,
|
||||
p.crClient,
|
||||
); err != nil {
|
||||
// #4764: Leave a warning when VolumeSnapshotContent cannot be found for deletion.
|
||||
// Manual deleting VolumeSnapshotContent can cause this.
|
||||
// It's tricky for Velero to handle this inconsistency.
|
||||
// Even if Velero restores the VolumeSnapshotContent, CSI snapshot controller
|
||||
// may not delete it correctly due to the snapshot represented by VolumeSnapshotContent
|
||||
// already deleted on cloud provider.
|
||||
if apierrors.IsNotFound(err) {
|
||||
p.log.Warnf(
|
||||
"VolumeSnapshotContent %s of backup %s cannot be found. May leave orphan snapshot %s on cloud provider.",
|
||||
snapCont.Name, input.Backup.Name, *snapCont.Status.SnapshotHandle)
|
||||
return nil
|
||||
}
|
||||
return errors.Wrapf(err, fmt.Sprintf(
|
||||
"failed to set DeletionPolicy on volumesnapshotcontent %s. Skipping deletion",
|
||||
snapCont.Name))
|
||||
}
|
||||
|
||||
if err := p.crClient.Delete(
|
||||
context.TODO(),
|
||||
&snapCont,
|
||||
); err != nil && !apierrors.IsNotFound(err) {
|
||||
p.log.Infof("VolumeSnapshotContent %s not found", snapCont.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewVolumeSnapshotContentDeleteItemAction(
|
||||
f client.Factory,
|
||||
) plugincommon.HandlerInitializer {
|
||||
return func(logger logrus.FieldLogger) (interface{}, error) {
|
||||
crClient, err := f.KubebuilderClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &volumeSnapshotContentDeleteItemAction{
|
||||
log: logger,
|
||||
crClient: crClient,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
Copyright the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package csi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v7/apis/volumesnapshot/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
)
|
||||
|
||||
func TestVSCExecute(t *testing.T) {
|
||||
snapshotHandleStr := "test"
|
||||
tests := []struct {
|
||||
name string
|
||||
item runtime.Unstructured
|
||||
vsc *snapshotv1api.VolumeSnapshotContent
|
||||
backup *velerov1api.Backup
|
||||
createVSC bool
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "VolumeSnapshotContent doesn't have backup label",
|
||||
item: velerotest.UnstructuredOrDie(
|
||||
`
|
||||
{
|
||||
"apiVersion": "snapshot.storage.k8s.io/v1",
|
||||
"kind": "VolumeSnapshotContent",
|
||||
"metadata": {
|
||||
"namespace": "ns",
|
||||
"name": "foo"
|
||||
}
|
||||
}
|
||||
`,
|
||||
),
|
||||
backup: builder.ForBackup("velero", "backup").Result(),
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "VolumeSnapshotContent doesn't exist in the cluster, no error",
|
||||
vsc: builder.ForVolumeSnapshotContent("bar").ObjectMeta(builder.WithLabelsMap(map[string]string{velerov1api.BackupNameLabel: "backup"})).Status(&snapshotv1api.VolumeSnapshotContentStatus{SnapshotHandle: &snapshotHandleStr}).Result(),
|
||||
backup: builder.ForBackup("velero", "backup").Result(),
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "Normal case, VolumeSnapshot should be deleted",
|
||||
vsc: builder.ForVolumeSnapshotContent("bar").ObjectMeta(builder.WithLabelsMap(map[string]string{velerov1api.BackupNameLabel: "backup"})).Status(&snapshotv1api.VolumeSnapshotContentStatus{SnapshotHandle: &snapshotHandleStr}).Result(),
|
||||
backup: builder.ForBackup("velero", "backup").Result(),
|
||||
expectErr: false,
|
||||
createVSC: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
crClient := velerotest.NewFakeControllerRuntimeClient(t)
|
||||
logger := logrus.StandardLogger()
|
||||
|
||||
p := volumeSnapshotContentDeleteItemAction{log: logger, crClient: crClient}
|
||||
|
||||
if test.vsc != nil {
|
||||
vscMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(test.vsc)
|
||||
require.NoError(t, err)
|
||||
test.item = &unstructured.Unstructured{Object: vscMap}
|
||||
}
|
||||
|
||||
if test.createVSC {
|
||||
require.NoError(t, crClient.Create(context.TODO(), test.vsc))
|
||||
}
|
||||
|
||||
err := p.Execute(
|
||||
&velero.DeleteItemActionExecuteInput{
|
||||
Item: test.item,
|
||||
Backup: test.backup,
|
||||
},
|
||||
)
|
||||
|
||||
if test.expectErr == false {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVSCAppliesTo(t *testing.T) {
|
||||
p := volumeSnapshotContentDeleteItemAction{
|
||||
log: logrus.StandardLogger(),
|
||||
}
|
||||
selector, err := p.AppliesTo()
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(
|
||||
t,
|
||||
velero.ResourceSelector{
|
||||
IncludedResources: []string{"volumesnapshotcontents.snapshot.storage.k8s.io"},
|
||||
},
|
||||
selector,
|
||||
)
|
||||
}
|
||||
|
||||
func TestNewVolumeSnapshotContentDeleteItemAction(t *testing.T) {
|
||||
logger := logrus.StandardLogger()
|
||||
crClient := velerotest.NewFakeControllerRuntimeClient(t)
|
||||
|
||||
f := &factorymocks.Factory{}
|
||||
f.On("KubebuilderClient").Return(nil, fmt.Errorf(""))
|
||||
plugin := NewVolumeSnapshotContentDeleteItemAction(f)
|
||||
_, err := plugin(logger)
|
||||
require.Error(t, err)
|
||||
|
||||
f1 := &factorymocks.Factory{}
|
||||
f1.On("KubebuilderClient").Return(crClient, nil)
|
||||
plugin1 := NewVolumeSnapshotContentDeleteItemAction(f1)
|
||||
_, err1 := plugin1(logger)
|
||||
require.NoError(t, err1)
|
||||
}
|
||||
@@ -119,7 +119,6 @@ func InvokeDeleteActions(ctx *Context) error {
|
||||
if !action.Selector.Matches(labels.Set(obj.GetLabels())) {
|
||||
continue
|
||||
}
|
||||
|
||||
err = action.DeleteItemAction.Execute(&velero.DeleteItemActionExecuteInput{
|
||||
Item: obj,
|
||||
Backup: ctx.Backup,
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package hook
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
HookSourceAnnotation = "annotation"
|
||||
HookSourceSpec = "spec"
|
||||
)
|
||||
|
||||
// hookKey identifies a backup/restore hook
|
||||
type hookKey struct {
|
||||
// PodNamespace indicates the namespace of pod where hooks are executed.
|
||||
// For hooks specified in the backup/restore spec, this field is the namespace of an applicable pod.
|
||||
// For hooks specified in pod annotation, this field is the namespace of pod where hooks are annotated.
|
||||
podNamespace string
|
||||
// PodName indicates the pod where hooks are executed.
|
||||
// For hooks specified in the backup/restore spec, this field is an applicable pod name.
|
||||
// For hooks specified in pod annotation, this field is the pod where hooks are annotated.
|
||||
podName string
|
||||
// HookPhase is only for backup hooks, for restore hooks, this field is empty.
|
||||
hookPhase hookPhase
|
||||
// HookName is only for hooks specified in the backup/restore spec.
|
||||
// For hooks specified in pod annotation, this field is empty or "<from-annotation>".
|
||||
hookName string
|
||||
// HookSource indicates where hooks come from.
|
||||
hookSource string
|
||||
// Container indicates the container hooks use.
|
||||
// For hooks specified in the backup/restore spec, the container might be the same under different hookName.
|
||||
container string
|
||||
}
|
||||
|
||||
// hookStatus records the execution status of a specific hook.
|
||||
// hookStatus is extensible to accommodate additional fields as needs develop.
|
||||
type hookStatus struct {
|
||||
// HookFailed indicates if hook failed to execute.
|
||||
hookFailed bool
|
||||
// hookExecuted indicates if hook already execute.
|
||||
hookExecuted bool
|
||||
}
|
||||
|
||||
// HookTracker tracks all hooks' execution status in a single backup/restore.
|
||||
type HookTracker struct {
|
||||
lock *sync.RWMutex
|
||||
// tracker records all hook info for a single backup/restore.
|
||||
tracker map[hookKey]hookStatus
|
||||
// hookAttemptedCnt indicates the number of attempted hooks.
|
||||
hookAttemptedCnt int
|
||||
// hookFailedCnt indicates the number of failed hooks.
|
||||
hookFailedCnt int
|
||||
// HookExecutedCnt indicates the number of executed hooks.
|
||||
hookExecutedCnt int
|
||||
// hookErrs records hook execution errors if any.
|
||||
hookErrs []HookErrInfo
|
||||
}
|
||||
|
||||
// NewHookTracker creates a hookTracker instance.
|
||||
func NewHookTracker() *HookTracker {
|
||||
return &HookTracker{
|
||||
lock: &sync.RWMutex{},
|
||||
tracker: make(map[hookKey]hookStatus),
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds a hook to the hook tracker
|
||||
// Add must precede the Record for each individual hook.
|
||||
// In other words, a hook must be added to the tracker before its execution result is recorded.
|
||||
func (ht *HookTracker) Add(podNamespace, podName, container, source, hookName string, hookPhase hookPhase) {
|
||||
ht.lock.Lock()
|
||||
defer ht.lock.Unlock()
|
||||
|
||||
key := hookKey{
|
||||
podNamespace: podNamespace,
|
||||
podName: podName,
|
||||
hookSource: source,
|
||||
container: container,
|
||||
hookPhase: hookPhase,
|
||||
hookName: hookName,
|
||||
}
|
||||
|
||||
if _, ok := ht.tracker[key]; !ok {
|
||||
ht.tracker[key] = hookStatus{
|
||||
hookFailed: false,
|
||||
hookExecuted: false,
|
||||
}
|
||||
ht.hookAttemptedCnt++
|
||||
}
|
||||
}
|
||||
|
||||
// Record records the hook's execution status
|
||||
// Add must precede the Record for each individual hook.
|
||||
// In other words, a hook must be added to the tracker before its execution result is recorded.
|
||||
func (ht *HookTracker) Record(podNamespace, podName, container, source, hookName string, hookPhase hookPhase, hookFailed bool, hookErr error) error {
|
||||
ht.lock.Lock()
|
||||
defer ht.lock.Unlock()
|
||||
|
||||
key := hookKey{
|
||||
podNamespace: podNamespace,
|
||||
podName: podName,
|
||||
hookSource: source,
|
||||
container: container,
|
||||
hookPhase: hookPhase,
|
||||
hookName: hookName,
|
||||
}
|
||||
|
||||
if _, ok := ht.tracker[key]; !ok {
|
||||
return fmt.Errorf("hook not exist in hook tracker, hook: %+v", key)
|
||||
}
|
||||
|
||||
if !ht.tracker[key].hookExecuted {
|
||||
ht.tracker[key] = hookStatus{
|
||||
hookFailed: hookFailed,
|
||||
hookExecuted: true,
|
||||
}
|
||||
ht.hookExecutedCnt++
|
||||
if hookFailed {
|
||||
ht.hookFailedCnt++
|
||||
ht.hookErrs = append(ht.hookErrs, HookErrInfo{Namespace: key.podNamespace, Err: hookErr})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stat returns the number of attempted hooks and failed hooks
|
||||
func (ht *HookTracker) Stat() (hookAttemptedCnt int, hookFailedCnt int) {
|
||||
ht.lock.RLock()
|
||||
defer ht.lock.RUnlock()
|
||||
|
||||
return ht.hookAttemptedCnt, ht.hookFailedCnt
|
||||
}
|
||||
|
||||
// IsComplete returns whether the execution of all hooks has finished or not
|
||||
func (ht *HookTracker) IsComplete() bool {
|
||||
ht.lock.RLock()
|
||||
defer ht.lock.RUnlock()
|
||||
|
||||
return ht.hookAttemptedCnt == ht.hookExecutedCnt
|
||||
}
|
||||
|
||||
// HooksErr returns hook execution errors
|
||||
func (ht *HookTracker) HookErrs() []HookErrInfo {
|
||||
ht.lock.RLock()
|
||||
defer ht.lock.RUnlock()
|
||||
|
||||
return ht.hookErrs
|
||||
}
|
||||
|
||||
// MultiHookTrackers tracks all hooks' execution status for multiple backups/restores.
|
||||
type MultiHookTracker struct {
|
||||
lock *sync.RWMutex
|
||||
// trackers is a map that uses the backup/restore name as the key and stores a HookTracker as value.
|
||||
trackers map[string]*HookTracker
|
||||
}
|
||||
|
||||
// NewMultiHookTracker creates a multiHookTracker instance.
|
||||
func NewMultiHookTracker() *MultiHookTracker {
|
||||
return &MultiHookTracker{
|
||||
lock: &sync.RWMutex{},
|
||||
trackers: make(map[string]*HookTracker),
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds a backup/restore hook to the tracker
|
||||
func (mht *MultiHookTracker) Add(name, podNamespace, podName, container, source, hookName string, hookPhase hookPhase) {
|
||||
mht.lock.Lock()
|
||||
defer mht.lock.Unlock()
|
||||
|
||||
if _, ok := mht.trackers[name]; !ok {
|
||||
mht.trackers[name] = NewHookTracker()
|
||||
}
|
||||
mht.trackers[name].Add(podNamespace, podName, container, source, hookName, hookPhase)
|
||||
}
|
||||
|
||||
// Record records a backup/restore hook execution status
|
||||
func (mht *MultiHookTracker) Record(name, podNamespace, podName, container, source, hookName string, hookPhase hookPhase, hookFailed bool, hookErr error) error {
|
||||
mht.lock.RLock()
|
||||
defer mht.lock.RUnlock()
|
||||
|
||||
var err error
|
||||
if _, ok := mht.trackers[name]; ok {
|
||||
err = mht.trackers[name].Record(podNamespace, podName, container, source, hookName, hookPhase, hookFailed, hookErr)
|
||||
} else {
|
||||
err = fmt.Errorf("the backup/restore not exist in hook tracker, backup/restore name: %s", name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Stat returns the number of attempted hooks and failed hooks for a particular backup/restore
|
||||
func (mht *MultiHookTracker) Stat(name string) (hookAttemptedCnt int, hookFailedCnt int) {
|
||||
mht.lock.RLock()
|
||||
defer mht.lock.RUnlock()
|
||||
|
||||
if _, ok := mht.trackers[name]; ok {
|
||||
return mht.trackers[name].Stat()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete removes the hook data for a particular backup/restore
|
||||
func (mht *MultiHookTracker) Delete(name string) {
|
||||
mht.lock.Lock()
|
||||
defer mht.lock.Unlock()
|
||||
|
||||
delete(mht.trackers, name)
|
||||
}
|
||||
|
||||
// IsComplete returns whether the execution of all hooks for a particular backup/restore has finished or not
|
||||
func (mht *MultiHookTracker) IsComplete(name string) bool {
|
||||
mht.lock.RLock()
|
||||
defer mht.lock.RUnlock()
|
||||
|
||||
if _, ok := mht.trackers[name]; ok {
|
||||
return mht.trackers[name].IsComplete()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// HooksErr returns hook execution errors for a particular backup/restore
|
||||
func (mht *MultiHookTracker) HookErrs(name string) []HookErrInfo {
|
||||
mht.lock.RLock()
|
||||
defer mht.lock.RUnlock()
|
||||
|
||||
if _, ok := mht.trackers[name]; ok {
|
||||
return mht.trackers[name].HookErrs()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package hook
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewHookTracker(t *testing.T) {
|
||||
tracker := NewHookTracker()
|
||||
|
||||
assert.NotNil(t, tracker)
|
||||
assert.Empty(t, tracker.tracker)
|
||||
}
|
||||
|
||||
func TestHookTracker_Add(t *testing.T) {
|
||||
tracker := NewHookTracker()
|
||||
|
||||
tracker.Add("ns1", "pod1", "container1", HookSourceAnnotation, "h1", "")
|
||||
|
||||
key := hookKey{
|
||||
podNamespace: "ns1",
|
||||
podName: "pod1",
|
||||
container: "container1",
|
||||
hookPhase: "",
|
||||
hookSource: HookSourceAnnotation,
|
||||
hookName: "h1",
|
||||
}
|
||||
|
||||
_, ok := tracker.tracker[key]
|
||||
assert.True(t, ok)
|
||||
}
|
||||
|
||||
func TestHookTracker_Record(t *testing.T) {
|
||||
tracker := NewHookTracker()
|
||||
tracker.Add("ns1", "pod1", "container1", HookSourceAnnotation, "h1", "")
|
||||
err := tracker.Record("ns1", "pod1", "container1", HookSourceAnnotation, "h1", "", true, fmt.Errorf("err"))
|
||||
|
||||
key := hookKey{
|
||||
podNamespace: "ns1",
|
||||
podName: "pod1",
|
||||
container: "container1",
|
||||
hookPhase: "",
|
||||
hookSource: HookSourceAnnotation,
|
||||
hookName: "h1",
|
||||
}
|
||||
|
||||
info := tracker.tracker[key]
|
||||
assert.True(t, info.hookFailed)
|
||||
assert.True(t, info.hookExecuted)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = tracker.Record("ns2", "pod2", "container1", HookSourceAnnotation, "h1", "", true, fmt.Errorf("err"))
|
||||
assert.NotNil(t, err)
|
||||
|
||||
err = tracker.Record("ns1", "pod1", "container1", HookSourceAnnotation, "h1", "", false, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, info.hookFailed)
|
||||
}
|
||||
|
||||
func TestHookTracker_Stat(t *testing.T) {
|
||||
tracker := NewHookTracker()
|
||||
|
||||
tracker.Add("ns1", "pod1", "container1", HookSourceAnnotation, "h1", "")
|
||||
tracker.Add("ns2", "pod2", "container1", HookSourceAnnotation, "h2", "")
|
||||
tracker.Record("ns1", "pod1", "container1", HookSourceAnnotation, "h1", "", true, fmt.Errorf("err"))
|
||||
|
||||
attempted, failed := tracker.Stat()
|
||||
assert.Equal(t, 2, attempted)
|
||||
assert.Equal(t, 1, failed)
|
||||
}
|
||||
|
||||
func TestHookTracker_IsComplete(t *testing.T) {
|
||||
tracker := NewHookTracker()
|
||||
tracker.Add("ns1", "pod1", "container1", HookSourceAnnotation, "h1", PhasePre)
|
||||
tracker.Record("ns1", "pod1", "container1", HookSourceAnnotation, "h1", PhasePre, true, fmt.Errorf("err"))
|
||||
assert.True(t, tracker.IsComplete())
|
||||
|
||||
tracker.Add("ns1", "pod1", "container1", HookSourceAnnotation, "h1", "")
|
||||
assert.False(t, tracker.IsComplete())
|
||||
}
|
||||
|
||||
func TestHookTracker_HookErrs(t *testing.T) {
|
||||
tracker := NewHookTracker()
|
||||
tracker.Add("ns1", "pod1", "container1", HookSourceAnnotation, "h1", "")
|
||||
tracker.Record("ns1", "pod1", "container1", HookSourceAnnotation, "h1", "", true, fmt.Errorf("err"))
|
||||
|
||||
hookErrs := tracker.HookErrs()
|
||||
assert.Len(t, hookErrs, 1)
|
||||
}
|
||||
|
||||
func TestMultiHookTracker_Add(t *testing.T) {
|
||||
mht := NewMultiHookTracker()
|
||||
|
||||
mht.Add("restore1", "ns1", "pod1", "container1", HookSourceAnnotation, "h1", "")
|
||||
|
||||
key := hookKey{
|
||||
podNamespace: "ns1",
|
||||
podName: "pod1",
|
||||
container: "container1",
|
||||
hookPhase: "",
|
||||
hookSource: HookSourceAnnotation,
|
||||
hookName: "h1",
|
||||
}
|
||||
|
||||
_, ok := mht.trackers["restore1"].tracker[key]
|
||||
assert.True(t, ok)
|
||||
}
|
||||
|
||||
func TestMultiHookTracker_Record(t *testing.T) {
|
||||
mht := NewMultiHookTracker()
|
||||
mht.Add("restore1", "ns1", "pod1", "container1", HookSourceAnnotation, "h1", "")
|
||||
err := mht.Record("restore1", "ns1", "pod1", "container1", HookSourceAnnotation, "h1", "", true, fmt.Errorf("err"))
|
||||
|
||||
key := hookKey{
|
||||
podNamespace: "ns1",
|
||||
podName: "pod1",
|
||||
container: "container1",
|
||||
hookPhase: "",
|
||||
hookSource: HookSourceAnnotation,
|
||||
hookName: "h1",
|
||||
}
|
||||
|
||||
info := mht.trackers["restore1"].tracker[key]
|
||||
assert.True(t, info.hookFailed)
|
||||
assert.True(t, info.hookExecuted)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = mht.Record("restore1", "ns2", "pod2", "container1", HookSourceAnnotation, "h1", "", true, fmt.Errorf("err"))
|
||||
assert.NotNil(t, err)
|
||||
|
||||
err = mht.Record("restore2", "ns2", "pod2", "container1", HookSourceAnnotation, "h1", "", true, fmt.Errorf("err"))
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestMultiHookTracker_Stat(t *testing.T) {
|
||||
mht := NewMultiHookTracker()
|
||||
|
||||
mht.Add("restore1", "ns1", "pod1", "container1", HookSourceAnnotation, "h1", "")
|
||||
mht.Add("restore1", "ns2", "pod2", "container1", HookSourceAnnotation, "h2", "")
|
||||
mht.Record("restore1", "ns1", "pod1", "container1", HookSourceAnnotation, "h1", "", true, fmt.Errorf("err"))
|
||||
mht.Record("restore1", "ns2", "pod2", "container1", HookSourceAnnotation, "h2", "", false, nil)
|
||||
|
||||
attempted, failed := mht.Stat("restore1")
|
||||
assert.Equal(t, 2, attempted)
|
||||
assert.Equal(t, 1, failed)
|
||||
}
|
||||
|
||||
func TestMultiHookTracker_Delete(t *testing.T) {
|
||||
mht := NewMultiHookTracker()
|
||||
mht.Add("restore1", "ns1", "pod1", "container1", HookSourceAnnotation, "h1", "")
|
||||
mht.Delete("restore1")
|
||||
|
||||
_, ok := mht.trackers["restore1"]
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestMultiHookTracker_IsComplete(t *testing.T) {
|
||||
mht := NewMultiHookTracker()
|
||||
mht.Add("backup1", "ns1", "pod1", "container1", HookSourceAnnotation, "h1", PhasePre)
|
||||
mht.Record("backup1", "ns1", "pod1", "container1", HookSourceAnnotation, "h1", PhasePre, true, fmt.Errorf("err"))
|
||||
assert.True(t, mht.IsComplete("backup1"))
|
||||
|
||||
mht.Add("restore1", "ns1", "pod1", "container1", HookSourceAnnotation, "h1", "")
|
||||
assert.False(t, mht.IsComplete("restore1"))
|
||||
|
||||
assert.True(t, mht.IsComplete("restore2"))
|
||||
}
|
||||
|
||||
func TestMultiHookTracker_HookErrs(t *testing.T) {
|
||||
mht := NewMultiHookTracker()
|
||||
mht.Add("restore1", "ns1", "pod1", "container1", HookSourceAnnotation, "h1", "")
|
||||
mht.Record("restore1", "ns1", "pod1", "container1", HookSourceAnnotation, "h1", "", true, fmt.Errorf("err"))
|
||||
|
||||
hookErrs := mht.HookErrs("restore1")
|
||||
assert.Len(t, hookErrs, 1)
|
||||
|
||||
hookErrs2 := mht.HookErrs("restore2")
|
||||
assert.Empty(t, hookErrs2)
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package hook
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -38,7 +37,6 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
||||
"github.com/vmware-tanzu/velero/pkg/podexec"
|
||||
"github.com/vmware-tanzu/velero/pkg/restorehelper"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/collections"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
)
|
||||
@@ -63,7 +61,6 @@ const (
|
||||
podRestoreHookOnErrorAnnotationKey = "post.hook.restore.velero.io/on-error"
|
||||
podRestoreHookTimeoutAnnotationKey = "post.hook.restore.velero.io/exec-timeout"
|
||||
podRestoreHookWaitTimeoutAnnotationKey = "post.hook.restore.velero.io/wait-timeout"
|
||||
podRestoreHookWaitForReadyAnnotationKey = "post.hook.restore.velero.io/wait-for-ready"
|
||||
podRestoreHookInitContainerImageAnnotationKey = "init.hook.restore.velero.io/container-image"
|
||||
podRestoreHookInitContainerNameAnnotationKey = "init.hook.restore.velero.io/container-name"
|
||||
podRestoreHookInitContainerCommandAnnotationKey = "init.hook.restore.velero.io/command"
|
||||
@@ -82,7 +79,6 @@ type ItemHookHandler interface {
|
||||
obj runtime.Unstructured,
|
||||
resourceHooks []ResourceHook,
|
||||
phase hookPhase,
|
||||
hookTracker *HookTracker,
|
||||
) error
|
||||
}
|
||||
|
||||
@@ -201,7 +197,6 @@ func (h *DefaultItemHookHandler) HandleHooks(
|
||||
obj runtime.Unstructured,
|
||||
resourceHooks []ResourceHook,
|
||||
phase hookPhase,
|
||||
hookTracker *HookTracker,
|
||||
) error {
|
||||
// We only support hooks on pods right now
|
||||
if groupResource != kuberesource.Pods {
|
||||
@@ -223,29 +218,18 @@ func (h *DefaultItemHookHandler) HandleHooks(
|
||||
hookFromAnnotations = getPodExecHookFromAnnotations(metadata.GetAnnotations(), "", log)
|
||||
}
|
||||
if hookFromAnnotations != nil {
|
||||
hookTracker.Add(namespace, name, hookFromAnnotations.Container, HookSourceAnnotation, "", phase)
|
||||
|
||||
hookLog := log.WithFields(
|
||||
logrus.Fields{
|
||||
"hookSource": HookSourceAnnotation,
|
||||
"hookSource": "annotation",
|
||||
"hookType": "exec",
|
||||
"hookPhase": phase,
|
||||
},
|
||||
)
|
||||
|
||||
hookFailed := false
|
||||
var errExec error
|
||||
if errExec = h.PodCommandExecutor.ExecutePodCommand(hookLog, obj.UnstructuredContent(), namespace, name, "<from-annotation>", hookFromAnnotations); errExec != nil {
|
||||
hookLog.WithError(errExec).Error("Error executing hook")
|
||||
hookFailed = true
|
||||
}
|
||||
errTracker := hookTracker.Record(namespace, name, hookFromAnnotations.Container, HookSourceAnnotation, "", phase, hookFailed, errExec)
|
||||
if errTracker != nil {
|
||||
hookLog.WithError(errTracker).Warn("Error recording the hook in hook tracker")
|
||||
}
|
||||
|
||||
if errExec != nil && hookFromAnnotations.OnError == velerov1api.HookErrorModeFail {
|
||||
return errExec
|
||||
if err := h.PodCommandExecutor.ExecutePodCommand(hookLog, obj.UnstructuredContent(), namespace, name, "<from-annotation>", hookFromAnnotations); err != nil {
|
||||
hookLog.WithError(err).Error("Error executing hook")
|
||||
if hookFromAnnotations.OnError == velerov1api.HookErrorModeFail {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -253,8 +237,6 @@ func (h *DefaultItemHookHandler) HandleHooks(
|
||||
|
||||
labels := labels.Set(metadata.GetLabels())
|
||||
// Otherwise, check for hooks defined in the backup spec.
|
||||
// modeFailError records the error from the hook with "Fail" error mode
|
||||
var modeFailError error
|
||||
for _, resourceHook := range resourceHooks {
|
||||
if !resourceHook.Selector.applicableTo(groupResource, namespace, labels) {
|
||||
continue
|
||||
@@ -266,34 +248,21 @@ func (h *DefaultItemHookHandler) HandleHooks(
|
||||
} else {
|
||||
hooks = resourceHook.Post
|
||||
}
|
||||
|
||||
for _, hook := range hooks {
|
||||
if groupResource == kuberesource.Pods {
|
||||
if hook.Exec != nil {
|
||||
hookTracker.Add(namespace, name, hook.Exec.Container, HookSourceSpec, resourceHook.Name, phase)
|
||||
// The remaining hooks will only be executed if modeFailError is nil.
|
||||
// Otherwise, execution will stop and only hook collection will occur.
|
||||
if modeFailError == nil {
|
||||
hookLog := log.WithFields(
|
||||
logrus.Fields{
|
||||
"hookSource": HookSourceSpec,
|
||||
"hookType": "exec",
|
||||
"hookPhase": phase,
|
||||
},
|
||||
)
|
||||
|
||||
hookFailed := false
|
||||
err := h.PodCommandExecutor.ExecutePodCommand(hookLog, obj.UnstructuredContent(), namespace, name, resourceHook.Name, hook.Exec)
|
||||
if err != nil {
|
||||
hookLog.WithError(err).Error("Error executing hook")
|
||||
hookFailed = true
|
||||
if hook.Exec.OnError == velerov1api.HookErrorModeFail {
|
||||
modeFailError = err
|
||||
}
|
||||
}
|
||||
errTracker := hookTracker.Record(namespace, name, hook.Exec.Container, HookSourceSpec, resourceHook.Name, phase, hookFailed, err)
|
||||
if errTracker != nil {
|
||||
hookLog.WithError(errTracker).Warn("Error recording the hook in hook tracker")
|
||||
hookLog := log.WithFields(
|
||||
logrus.Fields{
|
||||
"hookSource": "backupSpec",
|
||||
"hookType": "exec",
|
||||
"hookPhase": phase,
|
||||
},
|
||||
)
|
||||
err := h.PodCommandExecutor.ExecutePodCommand(hookLog, obj.UnstructuredContent(), namespace, name, resourceHook.Name, hook.Exec)
|
||||
if err != nil {
|
||||
hookLog.WithError(err).Error("Error executing hook")
|
||||
if hook.Exec.OnError == velerov1api.HookErrorModeFail {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,7 +270,7 @@ func (h *DefaultItemHookHandler) HandleHooks(
|
||||
}
|
||||
}
|
||||
|
||||
return modeFailError
|
||||
return nil
|
||||
}
|
||||
|
||||
// NoOpItemHookHandler is the an itemHookHandler for the Finalize controller where hooks don't run
|
||||
@@ -313,7 +282,6 @@ func (h *NoOpItemHookHandler) HandleHooks(
|
||||
obj runtime.Unstructured,
|
||||
resourceHooks []ResourceHook,
|
||||
phase hookPhase,
|
||||
hookTracker *HookTracker,
|
||||
) error {
|
||||
return nil
|
||||
}
|
||||
@@ -509,23 +477,12 @@ func getPodExecRestoreHookFromAnnotations(annotations map[string]string, log log
|
||||
}
|
||||
}
|
||||
|
||||
waitForReadyString := annotations[podRestoreHookWaitForReadyAnnotationKey]
|
||||
waitForReady := boolptr.False()
|
||||
if waitForReadyString != "" {
|
||||
var err error
|
||||
*waitForReady, err = strconv.ParseBool(waitForReadyString)
|
||||
if err != nil {
|
||||
log.Warn(errors.Wrapf(err, "Unable to parse wait for ready %s, ignoring", waitForReadyString))
|
||||
}
|
||||
}
|
||||
|
||||
return &velerov1api.ExecRestoreHook{
|
||||
Container: container,
|
||||
Command: parseStringToCommand(commandValue),
|
||||
OnError: onError,
|
||||
ExecTimeout: metav1.Duration{Duration: execTimeout},
|
||||
WaitTimeout: metav1.Duration{Duration: waitTimeout},
|
||||
WaitForReady: waitForReady,
|
||||
Container: container,
|
||||
Command: parseStringToCommand(commandValue),
|
||||
OnError: onError,
|
||||
ExecTimeout: metav1.Duration{Duration: execTimeout},
|
||||
WaitTimeout: metav1.Duration{Duration: waitTimeout},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,11 +497,9 @@ type PodExecRestoreHook struct {
|
||||
// container name. If an exec hook is defined in annotation that is used, else applicable exec
|
||||
// hooks from the restore resource are accumulated.
|
||||
func GroupRestoreExecHooks(
|
||||
restoreName string,
|
||||
resourceRestoreHooks []ResourceRestoreHook,
|
||||
pod *corev1api.Pod,
|
||||
log logrus.FieldLogger,
|
||||
hookTrack *MultiHookTracker,
|
||||
) (map[string][]PodExecRestoreHook, error) {
|
||||
byContainer := map[string][]PodExecRestoreHook{}
|
||||
|
||||
@@ -561,11 +516,10 @@ func GroupRestoreExecHooks(
|
||||
if hookFromAnnotation.Container == "" {
|
||||
hookFromAnnotation.Container = pod.Spec.Containers[0].Name
|
||||
}
|
||||
hookTrack.Add(restoreName, metadata.GetNamespace(), metadata.GetName(), hookFromAnnotation.Container, HookSourceAnnotation, "<from-annotation>", hookPhase(""))
|
||||
byContainer[hookFromAnnotation.Container] = []PodExecRestoreHook{
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: HookSourceAnnotation,
|
||||
HookSource: "annotation",
|
||||
Hook: *hookFromAnnotation,
|
||||
},
|
||||
}
|
||||
@@ -586,17 +540,12 @@ func GroupRestoreExecHooks(
|
||||
named := PodExecRestoreHook{
|
||||
HookName: rrh.Name,
|
||||
Hook: *rh.Exec,
|
||||
HookSource: HookSourceSpec,
|
||||
}
|
||||
// default to false if attr WaitForReady not set
|
||||
if named.Hook.WaitForReady == nil {
|
||||
named.Hook.WaitForReady = boolptr.False()
|
||||
HookSource: "backupSpec",
|
||||
}
|
||||
// default to first container in pod if unset, without mutating resource restore hook
|
||||
if named.Hook.Container == "" {
|
||||
named.Hook.Container = pod.Spec.Containers[0].Name
|
||||
}
|
||||
hookTrack.Add(restoreName, metadata.GetNamespace(), metadata.GetName(), named.Hook.Container, HookSourceSpec, rrh.Name, hookPhase(""))
|
||||
byContainer[named.Hook.Container] = append(byContainer[named.Hook.Container], named)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/collections"
|
||||
)
|
||||
|
||||
@@ -108,7 +107,6 @@ func TestHandleHooksSkips(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
hookTracker := NewHookTracker()
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
|
||||
@@ -119,7 +117,7 @@ func TestHandleHooksSkips(t *testing.T) {
|
||||
}
|
||||
|
||||
groupResource := schema.ParseGroupResource(test.groupResource)
|
||||
err := h.HandleHooks(velerotest.NewLogger(), groupResource, test.item, test.hooks, PhasePre, hookTracker)
|
||||
err := h.HandleHooks(velerotest.NewLogger(), groupResource, test.item, test.hooks, PhasePre)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
@@ -486,8 +484,7 @@ func TestHandleHooks(t *testing.T) {
|
||||
}
|
||||
|
||||
groupResource := schema.ParseGroupResource(test.groupResource)
|
||||
hookTracker := NewHookTracker()
|
||||
err := h.HandleHooks(velerotest.NewLogger(), groupResource, test.item, test.hooks, test.phase, hookTracker)
|
||||
err := h.HandleHooks(velerotest.NewLogger(), groupResource, test.item, test.hooks, test.phase)
|
||||
|
||||
if test.expectedError != nil {
|
||||
assert.EqualError(t, err, test.expectedError.Error())
|
||||
@@ -727,8 +724,7 @@ func TestGetPodExecRestoreHookFromAnnotations(t *testing.T) {
|
||||
podRestoreHookCommandAnnotationKey: "/usr/bin/foo",
|
||||
},
|
||||
expected: &velerov1api.ExecRestoreHook{
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
WaitForReady: boolptr.False(),
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -737,8 +733,7 @@ func TestGetPodExecRestoreHookFromAnnotations(t *testing.T) {
|
||||
podRestoreHookCommandAnnotationKey: `["a","b","c"]`,
|
||||
},
|
||||
expected: &velerov1api.ExecRestoreHook{
|
||||
Command: []string{"a", "b", "c"},
|
||||
WaitForReady: boolptr.False(),
|
||||
Command: []string{"a", "b", "c"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -748,9 +743,8 @@ func TestGetPodExecRestoreHookFromAnnotations(t *testing.T) {
|
||||
podRestoreHookOnErrorAnnotationKey: string(velerov1api.HookErrorModeContinue),
|
||||
},
|
||||
expected: &velerov1api.ExecRestoreHook{
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
WaitForReady: boolptr.False(),
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -760,9 +754,8 @@ func TestGetPodExecRestoreHookFromAnnotations(t *testing.T) {
|
||||
podRestoreHookOnErrorAnnotationKey: string(velerov1api.HookErrorModeFail),
|
||||
},
|
||||
expected: &velerov1api.ExecRestoreHook{
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeFail,
|
||||
WaitForReady: boolptr.False(),
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeFail,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -773,10 +766,9 @@ func TestGetPodExecRestoreHookFromAnnotations(t *testing.T) {
|
||||
podRestoreHookWaitTimeoutAnnotationKey: "1h",
|
||||
},
|
||||
expected: &velerov1api.ExecRestoreHook{
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
ExecTimeout: metav1.Duration{Duration: 45 * time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Hour},
|
||||
WaitForReady: boolptr.False(),
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
ExecTimeout: metav1.Duration{Duration: 45 * time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Hour},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -786,9 +778,8 @@ func TestGetPodExecRestoreHookFromAnnotations(t *testing.T) {
|
||||
podRestoreHookContainerAnnotationKey: "my-app",
|
||||
},
|
||||
expected: &velerov1api.ExecRestoreHook{
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
Container: "my-app",
|
||||
WaitForReady: boolptr.False(),
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
Container: "my-app",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -799,10 +790,9 @@ func TestGetPodExecRestoreHookFromAnnotations(t *testing.T) {
|
||||
podRestoreHookTimeoutAnnotationKey: "none",
|
||||
},
|
||||
expected: &velerov1api.ExecRestoreHook{
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
Container: "my-app",
|
||||
ExecTimeout: metav1.Duration{Duration: 0},
|
||||
WaitForReady: boolptr.False(),
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
Container: "my-app",
|
||||
ExecTimeout: metav1.Duration{Duration: 0},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -813,10 +803,9 @@ func TestGetPodExecRestoreHookFromAnnotations(t *testing.T) {
|
||||
podRestoreHookWaitTimeoutAnnotationKey: "none",
|
||||
},
|
||||
expected: &velerov1api.ExecRestoreHook{
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
Container: "my-app",
|
||||
ExecTimeout: metav1.Duration{Duration: 0},
|
||||
WaitForReady: boolptr.False(),
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
Container: "my-app",
|
||||
ExecTimeout: metav1.Duration{Duration: 0},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -853,7 +842,6 @@ func TestGroupRestoreExecHooks(t *testing.T) {
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeContinue),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
podRestoreHookWaitForReadyAnnotationKey, "true",
|
||||
)).
|
||||
Containers(&corev1api.Container{
|
||||
Name: "container1",
|
||||
@@ -863,14 +851,13 @@ func TestGroupRestoreExecHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: HookSourceAnnotation,
|
||||
HookSource: "annotation",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
WaitForReady: boolptr.True(),
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -894,14 +881,13 @@ func TestGroupRestoreExecHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: HookSourceAnnotation,
|
||||
HookSource: "annotation",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
WaitForReady: boolptr.False(),
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -935,14 +921,13 @@ func TestGroupRestoreExecHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "hook1",
|
||||
HookSource: HookSourceSpec,
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
WaitForReady: boolptr.False(),
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -975,14 +960,13 @@ func TestGroupRestoreExecHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "hook1",
|
||||
HookSource: HookSourceSpec,
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
WaitForReady: boolptr.False(),
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1023,14 +1007,13 @@ func TestGroupRestoreExecHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: HookSourceAnnotation,
|
||||
HookSource: "annotation",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
WaitForReady: boolptr.False(),
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1122,12 +1105,11 @@ func TestGroupRestoreExecHooks(t *testing.T) {
|
||||
RestoreHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Exec: &velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/aaa"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second * 4},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute * 4},
|
||||
WaitForReady: boolptr.True(),
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/aaa"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second * 4},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute * 4},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1142,63 +1124,57 @@ func TestGroupRestoreExecHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "hook1",
|
||||
HookSource: HookSourceSpec,
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeFail,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
WaitForReady: boolptr.False(),
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeFail,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
},
|
||||
},
|
||||
{
|
||||
HookName: "hook1",
|
||||
HookSource: HookSourceSpec,
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/bar"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second * 2},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute * 2},
|
||||
WaitForReady: boolptr.False(),
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/bar"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second * 2},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute * 2},
|
||||
},
|
||||
},
|
||||
{
|
||||
HookName: "hook2",
|
||||
HookSource: HookSourceSpec,
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/aaa"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second * 4},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute * 4},
|
||||
WaitForReady: boolptr.True(),
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/aaa"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second * 4},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute * 4},
|
||||
},
|
||||
},
|
||||
},
|
||||
"container2": {
|
||||
{
|
||||
HookName: "hook1",
|
||||
HookSource: HookSourceSpec,
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container2",
|
||||
Command: []string{"/usr/bin/baz"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second * 3},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Second * 3},
|
||||
WaitForReady: boolptr.False(),
|
||||
Container: "container2",
|
||||
Command: []string{"/usr/bin/baz"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second * 3},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Second * 3},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
hookTracker := NewMultiHookTracker()
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
actual, err := GroupRestoreExecHooks("restore1", tc.resourceRestoreHooks, tc.pod, velerotest.NewLogger(), hookTracker)
|
||||
actual, err := GroupRestoreExecHooks(tc.resourceRestoreHooks, tc.pod, velerotest.NewLogger())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tc.expected, actual)
|
||||
})
|
||||
@@ -1976,7 +1952,7 @@ func TestValidateContainer(t *testing.T) {
|
||||
expectedError := fmt.Errorf("invalid InitContainer in restore hook, it doesn't have Command, Name or Image field")
|
||||
|
||||
// valid string should return nil as result.
|
||||
assert.Nil(t, ValidateContainer([]byte(valid)))
|
||||
assert.Equal(t, nil, ValidateContainer([]byte(valid)))
|
||||
|
||||
// noName string should return expected error as result.
|
||||
assert.Equal(t, expectedError, ValidateContainer([]byte(noName)))
|
||||
@@ -1987,495 +1963,3 @@ func TestValidateContainer(t *testing.T) {
|
||||
// noCommand string should return expected error as result.
|
||||
assert.Equal(t, expectedError, ValidateContainer([]byte(noCommand)))
|
||||
}
|
||||
|
||||
func TestBackupHookTracker(t *testing.T) {
|
||||
type podWithHook struct {
|
||||
item runtime.Unstructured
|
||||
hooks []ResourceHook
|
||||
hookErrorsByContainer map[string]error
|
||||
expectedPodHook *velerov1api.ExecHook
|
||||
expectedPodHookError error
|
||||
expectedError error
|
||||
}
|
||||
test1 := []struct {
|
||||
name string
|
||||
phase hookPhase
|
||||
groupResource string
|
||||
pods []podWithHook
|
||||
hookTracker *HookTracker
|
||||
expectedHookAttempted int
|
||||
expectedHookFailed int
|
||||
}{
|
||||
{
|
||||
name: "a pod with spec hooks, no error",
|
||||
phase: PhasePre,
|
||||
groupResource: "pods",
|
||||
hookTracker: NewHookTracker(),
|
||||
expectedHookAttempted: 2,
|
||||
expectedHookFailed: 0,
|
||||
pods: []podWithHook{
|
||||
{
|
||||
item: velerotest.UnstructuredOrDie(`
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"namespace": "ns",
|
||||
"name": "name"
|
||||
}
|
||||
}`),
|
||||
hooks: []ResourceHook{
|
||||
{
|
||||
Name: "hook1",
|
||||
Pre: []velerov1api.BackupResourceHook{
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "1a",
|
||||
Command: []string{"pre-1a"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "1b",
|
||||
Command: []string{"pre-1b"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "a pod with spec hooks and same container under different hook name, no error",
|
||||
phase: PhasePre,
|
||||
groupResource: "pods",
|
||||
hookTracker: NewHookTracker(),
|
||||
expectedHookAttempted: 4,
|
||||
expectedHookFailed: 0,
|
||||
pods: []podWithHook{
|
||||
{
|
||||
item: velerotest.UnstructuredOrDie(`
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"namespace": "ns",
|
||||
"name": "name"
|
||||
}
|
||||
}`),
|
||||
hooks: []ResourceHook{
|
||||
{
|
||||
Name: "hook1",
|
||||
Pre: []velerov1api.BackupResourceHook{
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "1a",
|
||||
Command: []string{"pre-1a"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "1b",
|
||||
Command: []string{"pre-1b"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "hook2",
|
||||
Pre: []velerov1api.BackupResourceHook{
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "1a",
|
||||
Command: []string{"2a"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "2b",
|
||||
Command: []string{"2b"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "a pod with spec hooks, on error=fail",
|
||||
phase: PhasePre,
|
||||
groupResource: "pods",
|
||||
hookTracker: NewHookTracker(),
|
||||
expectedHookAttempted: 4,
|
||||
expectedHookFailed: 2,
|
||||
pods: []podWithHook{
|
||||
{
|
||||
item: velerotest.UnstructuredOrDie(`
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"namespace": "ns",
|
||||
"name": "name"
|
||||
}
|
||||
}`),
|
||||
hooks: []ResourceHook{
|
||||
{
|
||||
Name: "hook1",
|
||||
Pre: []velerov1api.BackupResourceHook{
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "1a",
|
||||
Command: []string{"1a"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
},
|
||||
},
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "1b",
|
||||
Command: []string{"1b"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "hook2",
|
||||
Pre: []velerov1api.BackupResourceHook{
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "2",
|
||||
Command: []string{"2"},
|
||||
OnError: velerov1api.HookErrorModeFail,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "hook3",
|
||||
Pre: []velerov1api.BackupResourceHook{
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "3",
|
||||
Command: []string{"3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hookErrorsByContainer: map[string]error{
|
||||
"1a": errors.New("1a error, but continue"),
|
||||
"2": errors.New("2 error, fail"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "a pod with annotation and spec hooks",
|
||||
phase: PhasePre,
|
||||
groupResource: "pods",
|
||||
hookTracker: NewHookTracker(),
|
||||
expectedHookAttempted: 1,
|
||||
expectedHookFailed: 0,
|
||||
pods: []podWithHook{
|
||||
{
|
||||
item: velerotest.UnstructuredOrDie(`
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"namespace": "ns",
|
||||
"name": "name",
|
||||
"annotations": {
|
||||
"hook.backup.velero.io/container": "c",
|
||||
"hook.backup.velero.io/command": "/bin/ls"
|
||||
}
|
||||
}
|
||||
}`),
|
||||
expectedPodHook: &velerov1api.ExecHook{
|
||||
Container: "c",
|
||||
Command: []string{"/bin/ls"},
|
||||
},
|
||||
hooks: []ResourceHook{
|
||||
{
|
||||
Name: "hook1",
|
||||
Pre: []velerov1api.BackupResourceHook{
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "1a",
|
||||
Command: []string{"1a"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
},
|
||||
},
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "1b",
|
||||
Command: []string{"1b"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "a pod with annotation, on error=fail",
|
||||
phase: PhasePre,
|
||||
groupResource: "pods",
|
||||
hookTracker: NewHookTracker(),
|
||||
expectedHookAttempted: 1,
|
||||
expectedHookFailed: 1,
|
||||
pods: []podWithHook{
|
||||
{
|
||||
item: velerotest.UnstructuredOrDie(`
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"namespace": "ns",
|
||||
"name": "name",
|
||||
"annotations": {
|
||||
"hook.backup.velero.io/container": "c",
|
||||
"hook.backup.velero.io/command": "/bin/ls",
|
||||
"hook.backup.velero.io/on-error": "Fail"
|
||||
}
|
||||
}
|
||||
}`),
|
||||
expectedPodHook: &velerov1api.ExecHook{
|
||||
Container: "c",
|
||||
Command: []string{"/bin/ls"},
|
||||
OnError: velerov1api.HookErrorModeFail,
|
||||
},
|
||||
expectedPodHookError: errors.New("pod hook error"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "two pods, one with annotation, the other with spec",
|
||||
phase: PhasePre,
|
||||
groupResource: "pods",
|
||||
hookTracker: NewHookTracker(),
|
||||
expectedHookAttempted: 3,
|
||||
expectedHookFailed: 1,
|
||||
pods: []podWithHook{
|
||||
{
|
||||
item: velerotest.UnstructuredOrDie(`
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"namespace": "ns",
|
||||
"name": "name",
|
||||
"annotations": {
|
||||
"hook.backup.velero.io/container": "c",
|
||||
"hook.backup.velero.io/command": "/bin/ls",
|
||||
"hook.backup.velero.io/on-error": "Fail"
|
||||
}
|
||||
}
|
||||
}`),
|
||||
expectedPodHook: &velerov1api.ExecHook{
|
||||
Container: "c",
|
||||
Command: []string{"/bin/ls"},
|
||||
OnError: velerov1api.HookErrorModeFail,
|
||||
},
|
||||
expectedPodHookError: errors.New("pod hook error"),
|
||||
},
|
||||
{
|
||||
item: velerotest.UnstructuredOrDie(`
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"namespace": "ns",
|
||||
"name": "name"
|
||||
}
|
||||
}`),
|
||||
hooks: []ResourceHook{
|
||||
{
|
||||
Name: "hook1",
|
||||
Pre: []velerov1api.BackupResourceHook{
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "1a",
|
||||
Command: []string{"pre-1a"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Exec: &velerov1api.ExecHook{
|
||||
Container: "1b",
|
||||
Command: []string{"pre-1b"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range test1 {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
|
||||
defer podCommandExecutor.AssertExpectations(t)
|
||||
|
||||
h := &DefaultItemHookHandler{
|
||||
PodCommandExecutor: podCommandExecutor,
|
||||
}
|
||||
|
||||
groupResource := schema.ParseGroupResource(test.groupResource)
|
||||
hookTracker := test.hookTracker
|
||||
|
||||
for _, pod := range test.pods {
|
||||
if pod.expectedPodHook != nil {
|
||||
podCommandExecutor.On("ExecutePodCommand", mock.Anything, pod.item.UnstructuredContent(), "ns", "name", "<from-annotation>", pod.expectedPodHook).Return(pod.expectedPodHookError)
|
||||
} else {
|
||||
hookLoop:
|
||||
for _, resourceHook := range pod.hooks {
|
||||
for _, hook := range resourceHook.Pre {
|
||||
hookError := pod.hookErrorsByContainer[hook.Exec.Container]
|
||||
podCommandExecutor.On("ExecutePodCommand", mock.Anything, pod.item.UnstructuredContent(), "ns", "name", resourceHook.Name, hook.Exec).Return(hookError)
|
||||
if hookError != nil && hook.Exec.OnError == velerov1api.HookErrorModeFail {
|
||||
break hookLoop
|
||||
}
|
||||
}
|
||||
for _, hook := range resourceHook.Post {
|
||||
hookError := pod.hookErrorsByContainer[hook.Exec.Container]
|
||||
podCommandExecutor.On("ExecutePodCommand", mock.Anything, pod.item.UnstructuredContent(), "ns", "name", resourceHook.Name, hook.Exec).Return(hookError)
|
||||
if hookError != nil && hook.Exec.OnError == velerov1api.HookErrorModeFail {
|
||||
break hookLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
h.HandleHooks(velerotest.NewLogger(), groupResource, pod.item, pod.hooks, test.phase, hookTracker)
|
||||
}
|
||||
actualAtemptted, actualFailed := hookTracker.Stat()
|
||||
assert.Equal(t, test.expectedHookAttempted, actualAtemptted)
|
||||
assert.Equal(t, test.expectedHookFailed, actualFailed)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRestoreHookTrackerAdd(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
resourceRestoreHooks []ResourceRestoreHook
|
||||
pod *corev1api.Pod
|
||||
hookTracker *MultiHookTracker
|
||||
expectedCnt int
|
||||
}{
|
||||
{
|
||||
name: "neither spec hooks nor annotations hooks are set",
|
||||
resourceRestoreHooks: nil,
|
||||
pod: builder.ForPod("default", "my-pod").Result(),
|
||||
hookTracker: NewMultiHookTracker(),
|
||||
expectedCnt: 0,
|
||||
},
|
||||
{
|
||||
name: "a hook specified in pod annotation",
|
||||
resourceRestoreHooks: nil,
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
podRestoreHookContainerAnnotationKey, "container1",
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeContinue),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
podRestoreHookWaitForReadyAnnotationKey, "true",
|
||||
)).
|
||||
Containers(&corev1api.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
Result(),
|
||||
hookTracker: NewMultiHookTracker(),
|
||||
expectedCnt: 1,
|
||||
},
|
||||
{
|
||||
name: "two hooks specified in restore spec",
|
||||
resourceRestoreHooks: []ResourceRestoreHook{
|
||||
{
|
||||
Name: "hook1",
|
||||
Selector: ResourceHookSelector{},
|
||||
RestoreHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Exec: &velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
},
|
||||
},
|
||||
{
|
||||
Exec: &velerov1api.ExecRestoreHook{
|
||||
Container: "container2",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
Containers(&corev1api.Container{
|
||||
Name: "container1",
|
||||
}, &corev1api.Container{
|
||||
Name: "container2",
|
||||
}).
|
||||
Result(),
|
||||
hookTracker: NewMultiHookTracker(),
|
||||
expectedCnt: 2,
|
||||
},
|
||||
{
|
||||
name: "both spec hooks and annotations hooks are set",
|
||||
resourceRestoreHooks: []ResourceRestoreHook{
|
||||
{
|
||||
Name: "hook1",
|
||||
Selector: ResourceHookSelector{},
|
||||
RestoreHooks: []velerov1api.RestoreResourceHook{
|
||||
{
|
||||
Exec: &velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo2"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
podRestoreHookContainerAnnotationKey, "container1",
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeContinue),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
podRestoreHookWaitForReadyAnnotationKey, "true",
|
||||
)).
|
||||
Containers(&corev1api.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
Result(),
|
||||
hookTracker: NewMultiHookTracker(),
|
||||
expectedCnt: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, _ = GroupRestoreExecHooks("restore1", tc.resourceRestoreHooks, tc.pod, velerotest.NewLogger(), tc.hookTracker)
|
||||
if _, ok := tc.hookTracker.trackers["restore1"]; !ok {
|
||||
return
|
||||
}
|
||||
tracker := tc.hookTracker.trackers["restore1"].tracker
|
||||
assert.Len(t, tracker, tc.expectedCnt)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import (
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/podexec"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
)
|
||||
|
||||
@@ -39,8 +38,6 @@ type WaitExecHookHandler interface {
|
||||
log logrus.FieldLogger,
|
||||
pod *v1.Pod,
|
||||
byContainer map[string][]PodExecRestoreHook,
|
||||
multiHookTracker *MultiHookTracker,
|
||||
restoreName string,
|
||||
) []error
|
||||
}
|
||||
|
||||
@@ -52,11 +49,6 @@ type DefaultListWatchFactory struct {
|
||||
PodsGetter cache.Getter
|
||||
}
|
||||
|
||||
type HookErrInfo struct {
|
||||
Namespace string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (d *DefaultListWatchFactory) NewListWatch(namespace string, selector fields.Selector) cache.ListerWatcher {
|
||||
return cache.NewListWatchFromClient(d.PodsGetter, "pods", namespace, selector)
|
||||
}
|
||||
@@ -75,8 +67,6 @@ func (e *DefaultWaitExecHookHandler) HandleHooks(
|
||||
log logrus.FieldLogger,
|
||||
pod *v1.Pod,
|
||||
byContainer map[string][]PodExecRestoreHook,
|
||||
multiHookTracker *MultiHookTracker,
|
||||
restoreName string,
|
||||
) []error {
|
||||
if pod == nil {
|
||||
return nil
|
||||
@@ -136,8 +126,8 @@ func (e *DefaultWaitExecHookHandler) HandleHooks(
|
||||
}
|
||||
|
||||
for containerName, hooks := range byContainer {
|
||||
if !isContainerUp(newPod, containerName, hooks) {
|
||||
podLog.Infof("Container %s is not up: post-restore hooks will not yet be executed", containerName)
|
||||
if !isContainerRunning(newPod, containerName) {
|
||||
podLog.Infof("Container %s is not running: post-restore hooks will not yet be executed", containerName)
|
||||
continue
|
||||
}
|
||||
podMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(newPod)
|
||||
@@ -167,14 +157,8 @@ func (e *DefaultWaitExecHookHandler) HandleHooks(
|
||||
if hook.Hook.WaitTimeout.Duration != 0 && time.Since(waitStart) > hook.Hook.WaitTimeout.Duration {
|
||||
err := fmt.Errorf("hook %s in container %s expired before executing", hook.HookName, hook.Hook.Container)
|
||||
hookLog.Error(err)
|
||||
errors = append(errors, err)
|
||||
|
||||
errTracker := multiHookTracker.Record(restoreName, newPod.Namespace, newPod.Name, hook.Hook.Container, hook.HookSource, hook.HookName, hookPhase(""), true, err)
|
||||
if errTracker != nil {
|
||||
hookLog.WithError(errTracker).Warn("Error recording the hook in hook tracker")
|
||||
}
|
||||
|
||||
if hook.Hook.OnError == velerov1api.HookErrorModeFail {
|
||||
errors = append(errors, err)
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
@@ -185,24 +169,13 @@ func (e *DefaultWaitExecHookHandler) HandleHooks(
|
||||
OnError: hook.Hook.OnError,
|
||||
Timeout: hook.Hook.ExecTimeout,
|
||||
}
|
||||
|
||||
hookFailed := false
|
||||
var hookErr error
|
||||
if hookErr = e.PodCommandExecutor.ExecutePodCommand(hookLog, podMap, pod.Namespace, pod.Name, hook.HookName, eh); hookErr != nil {
|
||||
hookLog.WithError(hookErr).Error("Error executing hook")
|
||||
hookErr = fmt.Errorf("hook %s in container %s failed to execute, err: %v", hook.HookName, hook.Hook.Container, hookErr)
|
||||
errors = append(errors, hookErr)
|
||||
hookFailed = true
|
||||
}
|
||||
|
||||
errTracker := multiHookTracker.Record(restoreName, newPod.Namespace, newPod.Name, hook.Hook.Container, hook.HookSource, hook.HookName, hookPhase(""), hookFailed, hookErr)
|
||||
if errTracker != nil {
|
||||
hookLog.WithError(errTracker).Warn("Error recording the hook in hook tracker")
|
||||
}
|
||||
|
||||
if hookErr != nil && hook.Hook.OnError == velerov1api.HookErrorModeFail {
|
||||
cancel()
|
||||
return
|
||||
if err := e.PodCommandExecutor.ExecutePodCommand(hookLog, podMap, pod.Namespace, pod.Name, hook.HookName, eh); err != nil {
|
||||
hookLog.WithError(err).Error("Error executing hook")
|
||||
if hook.Hook.OnError == velerov1api.HookErrorModeFail {
|
||||
errors = append(errors, err)
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
delete(byContainer, containerName)
|
||||
@@ -230,9 +203,10 @@ func (e *DefaultWaitExecHookHandler) HandleHooks(
|
||||
podWatcher.Run(ctx.Done())
|
||||
|
||||
// There are some cases where this function could return with unexecuted hooks: the pod may
|
||||
// be deleted, a hook could fail, or it may timeout waiting for
|
||||
// be deleted, a hook with OnError mode Fail could fail, or it may timeout waiting for
|
||||
// containers to become ready.
|
||||
// Each unexecuted hook is logged as an error and this error will be returned from this function.
|
||||
// Each unexecuted hook is logged as an error but only hooks with OnError mode Fail return
|
||||
// an error from this function.
|
||||
for _, hooks := range byContainer {
|
||||
for _, hook := range hooks {
|
||||
if hook.executed {
|
||||
@@ -246,14 +220,10 @@ func (e *DefaultWaitExecHookHandler) HandleHooks(
|
||||
"hookPhase": "post",
|
||||
},
|
||||
)
|
||||
|
||||
errTracker := multiHookTracker.Record(restoreName, pod.Namespace, pod.Name, hook.Hook.Container, hook.HookSource, hook.HookName, hookPhase(""), true, err)
|
||||
if errTracker != nil {
|
||||
hookLog.WithError(errTracker).Warn("Error recording the hook in hook tracker")
|
||||
}
|
||||
|
||||
hookLog.Error(err)
|
||||
errors = append(errors, err)
|
||||
if hook.Hook.OnError == velerov1api.HookErrorModeFail {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,24 +243,14 @@ func podHasContainer(pod *v1.Pod, containerName string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func isContainerUp(pod *v1.Pod, containerName string, hooks []PodExecRestoreHook) bool {
|
||||
func isContainerRunning(pod *v1.Pod, containerName string) bool {
|
||||
if pod == nil {
|
||||
return false
|
||||
}
|
||||
var waitForReady bool
|
||||
for _, hook := range hooks {
|
||||
if boolptr.IsSetToTrue(hook.Hook.WaitForReady) {
|
||||
waitForReady = true
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, cs := range pod.Status.ContainerStatuses {
|
||||
if cs.Name != containerName {
|
||||
continue
|
||||
}
|
||||
if waitForReady {
|
||||
return cs.Ready
|
||||
}
|
||||
return cs.State.Running != nil
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ import (
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
||||
)
|
||||
|
||||
type fakeListWatchFactory struct {
|
||||
@@ -98,7 +97,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: HookSourceAnnotation,
|
||||
HookSource: "annotation",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
@@ -167,7 +166,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: HookSourceAnnotation,
|
||||
HookSource: "annotation",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
@@ -209,10 +208,10 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
Result(),
|
||||
},
|
||||
},
|
||||
expectedErrors: []error{errors.New("hook <from-annotation> in container container1 failed to execute, err: pod hook error")},
|
||||
expectedErrors: []error{errors.New("pod hook error")},
|
||||
},
|
||||
{
|
||||
name: "should return error when hook from annotation fails with on error mode continue",
|
||||
name: "should return no error when hook from annotation fails with on error mode continue",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
@@ -236,7 +235,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: HookSourceAnnotation,
|
||||
HookSource: "annotation",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
@@ -278,7 +277,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
Result(),
|
||||
},
|
||||
},
|
||||
expectedErrors: []error{errors.New("hook <from-annotation> in container container1 failed to execute, err: pod hook error")},
|
||||
expectedErrors: nil,
|
||||
},
|
||||
{
|
||||
name: "should return no error when hook from annotation executes after 10ms wait for container to start",
|
||||
@@ -305,7 +304,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: HookSourceAnnotation,
|
||||
HookSource: "annotation",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
@@ -391,7 +390,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: HookSourceSpec,
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
@@ -422,7 +421,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return error when spec hook with wait timeout expires with OnError mode Continue",
|
||||
name: "should return no error when spec hook with wait timeout expires with OnError mode Continue",
|
||||
groupResource: "pods",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
@@ -435,12 +434,12 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
expectedErrors: []error{errors.New("hook my-hook-1 in container container1 in pod default/my-pod not executed: context deadline exceeded")},
|
||||
expectedErrors: nil,
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: HookSourceSpec,
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
@@ -471,7 +470,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: HookSourceSpec,
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
@@ -502,7 +501,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: HookSourceSpec,
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
@@ -515,8 +514,8 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
sharedHooksContextTimeout: time.Millisecond,
|
||||
},
|
||||
{
|
||||
name: "should return error when shared hooks context is canceled before spec hook with OnError mode Continue executes",
|
||||
expectedErrors: []error{errors.New("hook my-hook-1 in container container1 in pod default/my-pod not executed: context deadline exceeded")},
|
||||
name: "should return no error when shared hooks context is canceled before spec hook with OnError mode Continue executes",
|
||||
expectedErrors: nil,
|
||||
groupResource: "pods",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
@@ -533,7 +532,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: HookSourceSpec,
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
@@ -574,7 +573,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: HookSourceSpec,
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
@@ -584,7 +583,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
"container2": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: HookSourceSpec,
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container2",
|
||||
Command: []string{"/usr/bin/bar"},
|
||||
@@ -710,6 +709,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
source := fcache.NewFakeControllerSource()
|
||||
go func() {
|
||||
// This is the state of the pod that will be seen by the AddFunc handler.
|
||||
@@ -743,8 +743,7 @@ func TestWaitExecHandleHooks(t *testing.T) {
|
||||
defer ctxCancel()
|
||||
}
|
||||
|
||||
hookTracker := NewMultiHookTracker()
|
||||
errs := h.HandleHooks(ctx, velerotest.NewLogger(), test.initialPod, test.byContainer, hookTracker, "restore1")
|
||||
errs := h.HandleHooks(ctx, velerotest.NewLogger(), test.initialPod, test.byContainer)
|
||||
|
||||
// for i, ee := range test.expectedErrors {
|
||||
require.Len(t, errs, len(test.expectedErrors))
|
||||
@@ -791,13 +790,12 @@ func TestPodHasContainer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsContainerUp(t *testing.T) {
|
||||
func TestIsContainerRunning(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *v1.Pod
|
||||
container string
|
||||
expect bool
|
||||
hooks []PodExecRestoreHook
|
||||
}{
|
||||
{
|
||||
name: "should return true when running",
|
||||
@@ -811,49 +809,6 @@ func TestIsContainerUp(t *testing.T) {
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
hooks: []PodExecRestoreHook{},
|
||||
},
|
||||
{
|
||||
name: "should return false when running but not ready",
|
||||
container: "container1",
|
||||
expect: false,
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
Ready: false,
|
||||
}).
|
||||
Result(),
|
||||
hooks: []PodExecRestoreHook{
|
||||
{
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
WaitForReady: boolptr.True(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return true when running and ready",
|
||||
container: "container1",
|
||||
expect: true,
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
Ready: true,
|
||||
}).
|
||||
Result(),
|
||||
hooks: []PodExecRestoreHook{
|
||||
{
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
WaitForReady: boolptr.True(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return false when no state is set",
|
||||
@@ -865,7 +820,6 @@ func TestIsContainerUp(t *testing.T) {
|
||||
State: v1.ContainerState{},
|
||||
}).
|
||||
Result(),
|
||||
hooks: []PodExecRestoreHook{},
|
||||
},
|
||||
{
|
||||
name: "should return false when waiting",
|
||||
@@ -879,7 +833,6 @@ func TestIsContainerUp(t *testing.T) {
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
hooks: []PodExecRestoreHook{},
|
||||
},
|
||||
{
|
||||
name: "should return true when running and first container is terminated",
|
||||
@@ -899,12 +852,11 @@ func TestIsContainerUp(t *testing.T) {
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
hooks: []PodExecRestoreHook{},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := isContainerUp(test.pod, test.container, test.hooks)
|
||||
actual := isContainerRunning(test.pod, test.container)
|
||||
assert.Equal(t, actual, test.expect)
|
||||
})
|
||||
}
|
||||
@@ -997,286 +949,3 @@ func TestMaxHookWait(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRestoreHookTrackerUpdate(t *testing.T) {
|
||||
type change struct {
|
||||
// delta to wait since last change applied or pod added
|
||||
wait time.Duration
|
||||
updated *v1.Pod
|
||||
}
|
||||
type expectedExecution struct {
|
||||
hook *velerov1api.ExecHook
|
||||
name string
|
||||
error error
|
||||
pod *v1.Pod
|
||||
}
|
||||
|
||||
hookTracker1 := NewMultiHookTracker()
|
||||
hookTracker1.Add("restore1", "default", "my-pod", "container1", HookSourceAnnotation, "<from-annotation>", hookPhase(""))
|
||||
|
||||
hookTracker2 := NewMultiHookTracker()
|
||||
hookTracker2.Add("restore1", "default", "my-pod", "container1", HookSourceSpec, "my-hook-1", hookPhase(""))
|
||||
|
||||
hookTracker3 := NewMultiHookTracker()
|
||||
hookTracker3.Add("restore1", "default", "my-pod", "container1", HookSourceSpec, "my-hook-1", hookPhase(""))
|
||||
hookTracker3.Add("restore1", "default", "my-pod", "container2", HookSourceSpec, "my-hook-2", hookPhase(""))
|
||||
|
||||
hookTracker4 := NewMultiHookTracker()
|
||||
hookTracker4.Add("restore1", "default", "my-pod", "container1", HookSourceSpec, "my-hook-1", hookPhase(""))
|
||||
|
||||
tests1 := []struct {
|
||||
name string
|
||||
initialPod *v1.Pod
|
||||
groupResource string
|
||||
byContainer map[string][]PodExecRestoreHook
|
||||
expectedExecutions []expectedExecution
|
||||
hookTracker *MultiHookTracker
|
||||
expectedFailed int
|
||||
}{
|
||||
{
|
||||
name: "a hook executes successfully",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
podRestoreHookContainerAnnotationKey, "container1",
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeContinue),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
)).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
groupResource: "pods",
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: HookSourceAnnotation,
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{Duration: time.Second},
|
||||
WaitTimeout: metav1.Duration{Duration: time.Minute},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedExecutions: []expectedExecution{
|
||||
{
|
||||
name: "<from-annotation>",
|
||||
hook: &velerov1api.ExecHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
Timeout: metav1.Duration{Duration: time.Second},
|
||||
},
|
||||
error: nil,
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithResourceVersion("1")).
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
podRestoreHookContainerAnnotationKey, "container1",
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeContinue),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
)).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
},
|
||||
hookTracker: hookTracker1,
|
||||
expectedFailed: 0,
|
||||
},
|
||||
{
|
||||
name: "a hook with OnError mode Fail failed to execute",
|
||||
groupResource: "pods",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: HookSourceSpec,
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeFail,
|
||||
WaitTimeout: metav1.Duration{Duration: time.Millisecond},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hookTracker: hookTracker2,
|
||||
expectedFailed: 1,
|
||||
},
|
||||
{
|
||||
name: "a hook with OnError mode Continue failed to execute",
|
||||
groupResource: "pods",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: HookSourceSpec,
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
WaitTimeout: metav1.Duration{Duration: time.Millisecond},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hookTracker: hookTracker4,
|
||||
expectedFailed: 1,
|
||||
},
|
||||
{
|
||||
name: "two hooks with OnError mode Continue failed to execute",
|
||||
groupResource: "pods",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
Containers(&v1.Container{
|
||||
Name: "container2",
|
||||
}).
|
||||
// initially both are waiting
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container2",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: HookSourceSpec,
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
WaitTimeout: metav1.Duration{Duration: time.Millisecond},
|
||||
},
|
||||
},
|
||||
},
|
||||
"container2": {
|
||||
{
|
||||
HookName: "my-hook-2",
|
||||
HookSource: HookSourceSpec,
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container2",
|
||||
Command: []string{"/usr/bin/bar"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
WaitTimeout: metav1.Duration{Duration: time.Millisecond},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hookTracker: hookTracker3,
|
||||
expectedFailed: 2,
|
||||
},
|
||||
{
|
||||
name: "a hook was recorded before added to tracker",
|
||||
groupResource: "pods",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: HookSourceSpec,
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
WaitTimeout: metav1.Duration{Duration: time.Millisecond},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hookTracker: NewMultiHookTracker(),
|
||||
expectedFailed: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests1 {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
source := fcache.NewFakeControllerSource()
|
||||
go func() {
|
||||
// This is the state of the pod that will be seen by the AddFunc handler.
|
||||
source.Add(test.initialPod)
|
||||
}()
|
||||
|
||||
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
|
||||
defer podCommandExecutor.AssertExpectations(t)
|
||||
|
||||
h := &DefaultWaitExecHookHandler{
|
||||
PodCommandExecutor: podCommandExecutor,
|
||||
ListWatchFactory: &fakeListWatchFactory{source},
|
||||
}
|
||||
|
||||
for _, e := range test.expectedExecutions {
|
||||
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(e.pod)
|
||||
assert.Nil(t, err)
|
||||
podCommandExecutor.On("ExecutePodCommand", mock.Anything, obj, e.pod.Namespace, e.pod.Name, e.name, e.hook).Return(e.error)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
_ = h.HandleHooks(ctx, velerotest.NewLogger(), test.initialPod, test.byContainer, test.hookTracker, "restore1")
|
||||
_, actualFailed := test.hookTracker.Stat("restore1")
|
||||
assert.Equal(t, test.expectedFailed, actualFailed)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package resourcemodifiers
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
@@ -1,25 +1,10 @@
|
||||
/*
|
||||
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 resourcemodifiers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -86,22 +71,8 @@ func GetResourceModifiersFromConfig(cm *v1.ConfigMap) (*ResourceModifiers, error
|
||||
|
||||
func (p *ResourceModifiers) ApplyResourceModifierRules(obj *unstructured.Unstructured, groupResource string, scheme *runtime.Scheme, log logrus.FieldLogger) []error {
|
||||
var errs []error
|
||||
origin := obj
|
||||
// If there are more than one rules, we need to keep the original object for condition matching
|
||||
if len(p.ResourceModifierRules) > 1 {
|
||||
origin = obj.DeepCopy()
|
||||
}
|
||||
for _, rule := range p.ResourceModifierRules {
|
||||
matched, err := rule.match(origin, groupResource, log)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
} else if !matched {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Infof("Applying resource modifier patch on %s/%s", origin.GetNamespace(), origin.GetName())
|
||||
err = rule.applyPatch(obj, scheme, log)
|
||||
err := rule.apply(obj, groupResource, scheme, log)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
@@ -110,54 +81,59 @@ func (p *ResourceModifiers) ApplyResourceModifierRules(obj *unstructured.Unstruc
|
||||
return errs
|
||||
}
|
||||
|
||||
func (r *ResourceModifierRule) match(obj *unstructured.Unstructured, groupResource string, log logrus.FieldLogger) (bool, error) {
|
||||
func (r *ResourceModifierRule) apply(obj *unstructured.Unstructured, groupResource string, scheme *runtime.Scheme, log logrus.FieldLogger) error {
|
||||
ns := obj.GetNamespace()
|
||||
if ns != "" {
|
||||
namespaceInclusion := collections.NewIncludesExcludes().Includes(r.Conditions.Namespaces...)
|
||||
if !namespaceInclusion.ShouldInclude(ns) {
|
||||
return false, nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
g, err := glob.Compile(r.Conditions.GroupResource, '.')
|
||||
if err != nil {
|
||||
log.Errorf("Bad glob pattern of groupResource in condition, groupResource: %s, err: %s", r.Conditions.GroupResource, err)
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
|
||||
if !g.Match(groupResource) {
|
||||
return false, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.Conditions.ResourceNameRegex != "" {
|
||||
match, err := regexp.MatchString(r.Conditions.ResourceNameRegex, obj.GetName())
|
||||
if err != nil {
|
||||
return false, errors.Errorf("error in matching regex %s", err.Error())
|
||||
return errors.Errorf("error in matching regex %s", err.Error())
|
||||
}
|
||||
if !match {
|
||||
return false, nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if r.Conditions.LabelSelector != nil {
|
||||
selector, err := metav1.LabelSelectorAsSelector(r.Conditions.LabelSelector)
|
||||
if err != nil {
|
||||
return false, errors.Errorf("error in creating label selector %s", err.Error())
|
||||
return errors.Errorf("error in creating label selector %s", err.Error())
|
||||
}
|
||||
if !selector.Matches(labels.Set(obj.GetLabels())) {
|
||||
return false, nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
match, err := matchConditions(obj, r.Conditions.Matches, log)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return err
|
||||
} else if !match {
|
||||
log.Info("Conditions do not match, skip it")
|
||||
return false, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
log.Infof("Applying resource modifier patch on %s/%s", obj.GetNamespace(), obj.GetName())
|
||||
err = r.applyPatch(obj, scheme, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func matchConditions(u *unstructured.Unstructured, rules []MatchRule, _ logrus.FieldLogger) (bool, error) {
|
||||
|
||||
@@ -1,18 +1,3 @@
|
||||
/*
|
||||
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 resourcemodifiers
|
||||
|
||||
import (
|
||||
@@ -428,6 +413,7 @@ func TestGetResourceModifiersFromConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResourceModifiers_ApplyResourceModifierRules(t *testing.T) {
|
||||
|
||||
pvcStandardSc := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
@@ -456,20 +442,6 @@ func TestResourceModifiers_ApplyResourceModifierRules(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
pvcGoldSc := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "PersistentVolumeClaim",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "test-pvc",
|
||||
"namespace": "foo",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"storageClassName": "gold",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
deployNginxOneReplica := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
@@ -693,110 +665,6 @@ func TestResourceModifiers_ApplyResourceModifierRules(t *testing.T) {
|
||||
wantErr: false,
|
||||
wantObj: pvcPremiumSc.DeepCopy(),
|
||||
},
|
||||
{
|
||||
name: "pvc with standard storage class should be patched to premium, even when rules are [standard => premium, premium => gold]",
|
||||
fields: fields{
|
||||
Version: "v1",
|
||||
ResourceModifierRules: []ResourceModifierRule{
|
||||
{
|
||||
Conditions: Conditions{
|
||||
GroupResource: "persistentvolumeclaims",
|
||||
ResourceNameRegex: ".*",
|
||||
Matches: []MatchRule{
|
||||
{
|
||||
Path: "/spec/storageClassName",
|
||||
Value: "standard",
|
||||
},
|
||||
},
|
||||
},
|
||||
Patches: []JSONPatch{
|
||||
{
|
||||
Operation: "replace",
|
||||
Path: "/spec/storageClassName",
|
||||
Value: "premium",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Conditions: Conditions{
|
||||
GroupResource: "persistentvolumeclaims",
|
||||
ResourceNameRegex: ".*",
|
||||
Matches: []MatchRule{
|
||||
{
|
||||
Path: "/spec/storageClassName",
|
||||
Value: "premium",
|
||||
},
|
||||
},
|
||||
},
|
||||
Patches: []JSONPatch{
|
||||
{
|
||||
Operation: "replace",
|
||||
Path: "/spec/storageClassName",
|
||||
Value: "gold",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
obj: pvcStandardSc.DeepCopy(),
|
||||
groupResource: "persistentvolumeclaims",
|
||||
},
|
||||
wantErr: false,
|
||||
wantObj: pvcPremiumSc.DeepCopy(),
|
||||
},
|
||||
{
|
||||
name: "pvc with standard storage class should be patched to gold, even when rules are [standard => premium, standard => gold]",
|
||||
fields: fields{
|
||||
Version: "v1",
|
||||
ResourceModifierRules: []ResourceModifierRule{
|
||||
{
|
||||
Conditions: Conditions{
|
||||
GroupResource: "persistentvolumeclaims",
|
||||
ResourceNameRegex: ".*",
|
||||
Matches: []MatchRule{
|
||||
{
|
||||
Path: "/spec/storageClassName",
|
||||
Value: "standard",
|
||||
},
|
||||
},
|
||||
},
|
||||
Patches: []JSONPatch{
|
||||
{
|
||||
Operation: "replace",
|
||||
Path: "/spec/storageClassName",
|
||||
Value: "premium",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Conditions: Conditions{
|
||||
GroupResource: "persistentvolumeclaims",
|
||||
ResourceNameRegex: ".*",
|
||||
Matches: []MatchRule{
|
||||
{
|
||||
Path: "/spec/storageClassName",
|
||||
Value: "standard",
|
||||
},
|
||||
},
|
||||
},
|
||||
Patches: []JSONPatch{
|
||||
{
|
||||
Operation: "replace",
|
||||
Path: "/spec/storageClassName",
|
||||
Value: "gold",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
obj: pvcStandardSc.DeepCopy(),
|
||||
groupResource: "persistentvolumeclaims",
|
||||
},
|
||||
wantErr: false,
|
||||
wantObj: pvcGoldSc.DeepCopy(),
|
||||
},
|
||||
{
|
||||
name: "nginx deployment: 1 -> 2 replicas",
|
||||
fields: fields{
|
||||
|
||||
@@ -1,18 +1,3 @@
|
||||
/*
|
||||
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 resourcemodifiers
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,18 +1,3 @@
|
||||
/*
|
||||
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 resourcemodifiers
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,44 +1,19 @@
|
||||
/*
|
||||
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 resourcepolicies
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
crclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
type VolumeActionType string
|
||||
|
||||
const (
|
||||
// currently only support configmap type of resource config
|
||||
ConfigmapRefType string = "configmap"
|
||||
// skip action implies the volume would be skipped from the backup operation
|
||||
Skip VolumeActionType = "skip"
|
||||
// fs-backup action implies that the volume would be backed up via file system copy method using the uploader(kopia/restic) configured by the user
|
||||
FSBackup VolumeActionType = "fs-backup"
|
||||
// snapshot action can have 3 different meaning based on velero configuration and backup spec - cloud provider based snapshots, local csi snapshots and datamover snapshots
|
||||
Snapshot VolumeActionType = "snapshot"
|
||||
ConfigmapRefType string = "configmap"
|
||||
Skip VolumeActionType = "skip"
|
||||
)
|
||||
|
||||
// Action defined as one action for a specific way of backup
|
||||
@@ -50,16 +25,16 @@ type Action struct {
|
||||
}
|
||||
|
||||
// volumePolicy defined policy to conditions to match Volumes and related action to handle matched Volumes
|
||||
type VolumePolicy struct {
|
||||
type volumePolicy struct {
|
||||
// Conditions defined list of conditions to match Volumes
|
||||
Conditions map[string]interface{} `yaml:"conditions"`
|
||||
Action Action `yaml:"action"`
|
||||
}
|
||||
|
||||
// resourcePolicies currently defined slice of volume policies to handle backup
|
||||
type ResourcePolicies struct {
|
||||
type resourcePolicies struct {
|
||||
Version string `yaml:"version"`
|
||||
VolumePolicies []VolumePolicy `yaml:"volumePolicies"`
|
||||
VolumePolicies []volumePolicy `yaml:"volumePolicies"`
|
||||
// we may support other resource policies in the future, and they could be added separately
|
||||
// OtherResourcePolicies []OtherResourcePolicy
|
||||
}
|
||||
@@ -70,8 +45,8 @@ type Policies struct {
|
||||
// OtherPolicies
|
||||
}
|
||||
|
||||
func unmarshalResourcePolicies(yamlData *string) (*ResourcePolicies, error) {
|
||||
resPolicies := &ResourcePolicies{}
|
||||
func unmarshalResourcePolicies(yamlData *string) (*resourcePolicies, error) {
|
||||
resPolicies := &resourcePolicies{}
|
||||
err := decodeStruct(strings.NewReader(*yamlData), resPolicies)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode yaml data into resource policies %v", err)
|
||||
@@ -79,7 +54,7 @@ func unmarshalResourcePolicies(yamlData *string) (*ResourcePolicies, error) {
|
||||
return resPolicies, nil
|
||||
}
|
||||
|
||||
func (p *Policies) BuildPolicy(resPolicies *ResourcePolicies) error {
|
||||
func (p *Policies) buildPolicy(resPolicies *resourcePolicies) error {
|
||||
for _, vp := range resPolicies.VolumePolicies {
|
||||
con, err := unmarshalVolConditions(vp.Conditions)
|
||||
if err != nil {
|
||||
@@ -95,7 +70,6 @@ func (p *Policies) BuildPolicy(resPolicies *ResourcePolicies) error {
|
||||
volP.conditions = append(volP.conditions, &storageClassCondition{storageClass: con.StorageClass})
|
||||
volP.conditions = append(volP.conditions, &nfsCondition{nfs: con.NFS})
|
||||
volP.conditions = append(volP.conditions, &csiCondition{csi: con.CSI})
|
||||
volP.conditions = append(volP.conditions, &volumeTypeCondition{volumeTypes: con.VolumeTypes})
|
||||
p.volumePolicies = append(p.volumePolicies, volP)
|
||||
}
|
||||
|
||||
@@ -153,43 +127,7 @@ func (p *Policies) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetResourcePoliciesFromBackup(
|
||||
backup velerov1api.Backup,
|
||||
client crclient.Client,
|
||||
logger logrus.FieldLogger,
|
||||
) (resourcePolicies *Policies, err error) {
|
||||
if backup.Spec.ResourcePolicy != nil &&
|
||||
strings.EqualFold(backup.Spec.ResourcePolicy.Kind, ConfigmapRefType) {
|
||||
policiesConfigMap := &v1.ConfigMap{}
|
||||
err = client.Get(
|
||||
context.Background(),
|
||||
crclient.ObjectKey{Namespace: backup.Namespace, Name: backup.Spec.ResourcePolicy.Name},
|
||||
policiesConfigMap,
|
||||
)
|
||||
if err != nil {
|
||||
logger.Errorf("Fail to get ResourcePolicies %s ConfigMap with error %s.",
|
||||
backup.Namespace+"/"+backup.Spec.ResourcePolicy.Name, err.Error())
|
||||
return nil, fmt.Errorf("fail to get ResourcePolicies %s ConfigMap with error %s",
|
||||
backup.Namespace+"/"+backup.Spec.ResourcePolicy.Name, err.Error())
|
||||
}
|
||||
resourcePolicies, err = getResourcePoliciesFromConfig(policiesConfigMap)
|
||||
if err != nil {
|
||||
logger.Errorf("Fail to read ResourcePolicies from ConfigMap %s with error %s.",
|
||||
backup.Namespace+"/"+backup.Name, err.Error())
|
||||
return nil, fmt.Errorf("fail to read the ResourcePolicies from ConfigMap %s with error %s",
|
||||
backup.Namespace+"/"+backup.Name, err.Error())
|
||||
} else if err = resourcePolicies.Validate(); err != nil {
|
||||
logger.Errorf("Fail to validate ResourcePolicies in ConfigMap %s with error %s.",
|
||||
backup.Namespace+"/"+backup.Name, err.Error())
|
||||
return nil, fmt.Errorf("fail to validate ResourcePolicies in ConfigMap %s with error %s",
|
||||
backup.Namespace+"/"+backup.Name, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return resourcePolicies, nil
|
||||
}
|
||||
|
||||
func getResourcePoliciesFromConfig(cm *v1.ConfigMap) (*Policies, error) {
|
||||
func GetResourcePoliciesFromConfig(cm *v1.ConfigMap) (*Policies, error) {
|
||||
if cm == nil {
|
||||
return nil, fmt.Errorf("could not parse config from nil configmap")
|
||||
}
|
||||
@@ -208,7 +146,7 @@ func getResourcePoliciesFromConfig(cm *v1.ConfigMap) (*Policies, error) {
|
||||
}
|
||||
|
||||
policies := &Policies{}
|
||||
if err := policies.BuildPolicy(resPolicies); err != nil {
|
||||
if err := policies.buildPolicy(resPolicies); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,3 @@
|
||||
/*
|
||||
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 resourcepolicies
|
||||
|
||||
import (
|
||||
@@ -121,9 +106,9 @@ func TestLoadResourcePolicies(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetResourceMatchedAction(t *testing.T) {
|
||||
resPolicies := &ResourcePolicies{
|
||||
resPolicies := &resourcePolicies{
|
||||
Version: "v1",
|
||||
VolumePolicies: []VolumePolicy{
|
||||
VolumePolicies: []volumePolicy{
|
||||
{
|
||||
Action: Action{Type: "skip"},
|
||||
Conditions: map[string]interface{}{
|
||||
@@ -136,7 +121,7 @@ func TestGetResourceMatchedAction(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Action: Action{Type: "snapshot"},
|
||||
Action: Action{Type: "volume-snapshot"},
|
||||
Conditions: map[string]interface{}{
|
||||
"capacity": "10,100Gi",
|
||||
"storageClass": []string{"gp2", "ebs-sc"},
|
||||
@@ -147,7 +132,7 @@ func TestGetResourceMatchedAction(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Action: Action{Type: "fs-backup"},
|
||||
Action: Action{Type: "file-system-backup"},
|
||||
Conditions: map[string]interface{}{
|
||||
"storageClass": []string{"gp2", "ebs-sc"},
|
||||
"csi": interface{}(
|
||||
@@ -179,10 +164,10 @@ func TestGetResourceMatchedAction(t *testing.T) {
|
||||
storageClass: "ebs-sc",
|
||||
csi: &csiVolumeSource{Driver: "aws.efs.csi.driver"},
|
||||
},
|
||||
expectedAction: &Action{Type: "snapshot"},
|
||||
expectedAction: &Action{Type: "volume-snapshot"},
|
||||
},
|
||||
{
|
||||
name: "mismatch all policies",
|
||||
name: "dismatch all policies",
|
||||
volume: &structuredVolume{
|
||||
capacity: *resource.NewQuantity(50<<30, resource.BinarySI),
|
||||
storageClass: "ebs-sc",
|
||||
@@ -195,7 +180,7 @@ func TestGetResourceMatchedAction(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
policies := &Policies{}
|
||||
err := policies.BuildPolicy(resPolicies)
|
||||
err := policies.buildPolicy(resPolicies)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to build policy with error %v", err)
|
||||
}
|
||||
@@ -231,15 +216,15 @@ func TestGetResourcePoliciesFromConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
// Call the function and check for errors
|
||||
resPolicies, err := getResourcePoliciesFromConfig(cm)
|
||||
resPolicies, err := GetResourcePoliciesFromConfig(cm)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Check that the returned resourcePolicies object contains the expected data
|
||||
assert.Equal(t, "v1", resPolicies.version)
|
||||
assert.Len(t, resPolicies.volumePolicies, 1)
|
||||
policies := ResourcePolicies{
|
||||
policies := resourcePolicies{
|
||||
Version: "v1",
|
||||
VolumePolicies: []VolumePolicy{
|
||||
VolumePolicies: []volumePolicy{
|
||||
{
|
||||
Conditions: map[string]interface{}{
|
||||
"capacity": "0,10Gi",
|
||||
@@ -251,7 +236,7 @@ func TestGetResourcePoliciesFromConfig(t *testing.T) {
|
||||
},
|
||||
}
|
||||
p := &Policies{}
|
||||
err = p.BuildPolicy(&policies)
|
||||
err = p.buildPolicy(&policies)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to build policy with error %v", err)
|
||||
}
|
||||
@@ -370,51 +355,6 @@ volumePolicies:
|
||||
},
|
||||
skip: false,
|
||||
},
|
||||
{
|
||||
name: "match volume by types",
|
||||
yamlData: `version: v1
|
||||
volumePolicies:
|
||||
- conditions:
|
||||
capacity: "0,100Gi"
|
||||
volumeTypes:
|
||||
- local
|
||||
- hostPath
|
||||
action:
|
||||
type: skip`,
|
||||
vol: &v1.PersistentVolume{
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
Capacity: v1.ResourceList{
|
||||
v1.ResourceStorage: resource.MustParse("1Gi"),
|
||||
},
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{Path: "/mnt/data"},
|
||||
},
|
||||
},
|
||||
},
|
||||
skip: true,
|
||||
},
|
||||
{
|
||||
name: "mismatch volume by types",
|
||||
yamlData: `version: v1
|
||||
volumePolicies:
|
||||
- conditions:
|
||||
capacity: "0,100Gi"
|
||||
volumeTypes:
|
||||
- local
|
||||
action:
|
||||
type: skip`,
|
||||
vol: &v1.PersistentVolume{
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
Capacity: v1.ResourceList{
|
||||
v1.ResourceStorage: resource.MustParse("1Gi"),
|
||||
},
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{Path: "/mnt/data"},
|
||||
},
|
||||
},
|
||||
},
|
||||
skip: false,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -424,7 +364,7 @@ volumePolicies:
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
policies := &Policies{}
|
||||
err = policies.BuildPolicy(resPolicies)
|
||||
err = policies.buildPolicy(resPolicies)
|
||||
assert.Nil(t, err)
|
||||
action, err := policies.GetMatchAction(tc.vol)
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -1,18 +1,3 @@
|
||||
/*
|
||||
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 resourcepolicies
|
||||
|
||||
import (
|
||||
@@ -47,7 +32,6 @@ type structuredVolume struct {
|
||||
storageClass string
|
||||
nfs *nFSVolumeSource
|
||||
csi *csiVolumeSource
|
||||
volumeType SupportedVolume
|
||||
}
|
||||
|
||||
func (s *structuredVolume) parsePV(pv *corev1api.PersistentVolume) {
|
||||
@@ -62,8 +46,6 @@ func (s *structuredVolume) parsePV(pv *corev1api.PersistentVolume) {
|
||||
if csi != nil {
|
||||
s.csi = &csiVolumeSource{Driver: csi.Driver}
|
||||
}
|
||||
|
||||
s.volumeType = getVolumeTypeFromPV(pv)
|
||||
}
|
||||
|
||||
func (s *structuredVolume) parsePodVolume(vol *corev1api.Volume) {
|
||||
@@ -76,8 +58,6 @@ func (s *structuredVolume) parsePodVolume(vol *corev1api.Volume) {
|
||||
if csi != nil {
|
||||
s.csi = &csiVolumeSource{Driver: csi.Driver}
|
||||
}
|
||||
|
||||
s.volumeType = getVolumeTypeFromVolume(vol)
|
||||
}
|
||||
|
||||
type capacityCondition struct {
|
||||
|
||||
@@ -1,18 +1,3 @@
|
||||
/*
|
||||
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 resourcepolicies
|
||||
|
||||
import (
|
||||
@@ -86,6 +71,7 @@ func TestCapacityIsInRange(t *testing.T) {
|
||||
actual := test.capacity.isInRange(test.quantity)
|
||||
|
||||
assert.Equal(t, test.isInRange, actual)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -140,6 +126,7 @@ func TestStorageClassConditionMatch(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNFSConditionMatch(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
condition *nfsCondition
|
||||
@@ -165,7 +152,7 @@ func TestNFSConditionMatch(t *testing.T) {
|
||||
expectedMatch: true,
|
||||
},
|
||||
{
|
||||
name: "server mismatch",
|
||||
name: "server dismatch",
|
||||
condition: &nfsCondition{&nFSVolumeSource{Server: "192.168.10.20", Path: ""}},
|
||||
volume: setStructuredVolume(*resource.NewQuantity(0, resource.BinarySI), "", &nFSVolumeSource{Server: ""}, nil),
|
||||
expectedMatch: false,
|
||||
@@ -194,6 +181,7 @@ func TestNFSConditionMatch(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCSIConditionMatch(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
condition *csiCondition
|
||||
|
||||
@@ -1,18 +1,3 @@
|
||||
/*
|
||||
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 resourcepolicies
|
||||
|
||||
import (
|
||||
@@ -38,11 +23,10 @@ type nFSVolumeSource struct {
|
||||
|
||||
// volumeConditions defined the current format of conditions we parsed
|
||||
type volumeConditions struct {
|
||||
Capacity string `yaml:"capacity,omitempty"`
|
||||
StorageClass []string `yaml:"storageClass,omitempty"`
|
||||
NFS *nFSVolumeSource `yaml:"nfs,omitempty"`
|
||||
CSI *csiVolumeSource `yaml:"csi,omitempty"`
|
||||
VolumeTypes []SupportedVolume `yaml:"volumeTypes,omitempty"`
|
||||
Capacity string `yaml:"capacity,omitempty"`
|
||||
StorageClass []string `yaml:"storageClass,omitempty"`
|
||||
NFS *nFSVolumeSource `yaml:"nfs,omitempty"`
|
||||
CSI *csiVolumeSource `yaml:"csi,omitempty"`
|
||||
}
|
||||
|
||||
func (c *capacityCondition) validate() error {
|
||||
@@ -82,11 +66,7 @@ func decodeStruct(r io.Reader, s interface{}) error {
|
||||
// validate check action format
|
||||
func (a *Action) validate() error {
|
||||
// validate Type
|
||||
valid := false
|
||||
if a.Type == Skip || a.Type == Snapshot || a.Type == FSBackup {
|
||||
valid = true
|
||||
}
|
||||
if !valid {
|
||||
if a.Type != Skip {
|
||||
return fmt.Errorf("invalid action type %s", a.Type)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,3 @@
|
||||
/*
|
||||
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 resourcepolicies
|
||||
|
||||
import (
|
||||
@@ -84,14 +69,14 @@ func TestCapacityConditionValidate(t *testing.T) {
|
||||
func TestValidate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
res *ResourcePolicies
|
||||
res *resourcePolicies
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "unknown key in yaml",
|
||||
res: &ResourcePolicies{
|
||||
res: &resourcePolicies{
|
||||
Version: "v1",
|
||||
VolumePolicies: []VolumePolicy{
|
||||
VolumePolicies: []volumePolicy{
|
||||
{
|
||||
Action: Action{Type: "skip"},
|
||||
Conditions: map[string]interface{}{
|
||||
@@ -110,9 +95,9 @@ func TestValidate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "error format of capacity",
|
||||
res: &ResourcePolicies{
|
||||
res: &resourcePolicies{
|
||||
Version: "v1",
|
||||
VolumePolicies: []VolumePolicy{
|
||||
VolumePolicies: []volumePolicy{
|
||||
{
|
||||
Action: Action{Type: "skip"},
|
||||
Conditions: map[string]interface{}{
|
||||
@@ -130,9 +115,9 @@ func TestValidate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "error format of storageClass",
|
||||
res: &ResourcePolicies{
|
||||
res: &resourcePolicies{
|
||||
Version: "v1",
|
||||
VolumePolicies: []VolumePolicy{
|
||||
VolumePolicies: []volumePolicy{
|
||||
{
|
||||
Action: Action{Type: "skip"},
|
||||
Conditions: map[string]interface{}{
|
||||
@@ -150,9 +135,9 @@ func TestValidate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "error format of csi",
|
||||
res: &ResourcePolicies{
|
||||
res: &resourcePolicies{
|
||||
Version: "v1",
|
||||
VolumePolicies: []VolumePolicy{
|
||||
VolumePolicies: []volumePolicy{
|
||||
{
|
||||
Action: Action{Type: "skip"},
|
||||
Conditions: map[string]interface{}{
|
||||
@@ -167,9 +152,9 @@ func TestValidate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "unsupported version",
|
||||
res: &ResourcePolicies{
|
||||
res: &resourcePolicies{
|
||||
Version: "v2",
|
||||
VolumePolicies: []VolumePolicy{
|
||||
VolumePolicies: []volumePolicy{
|
||||
{
|
||||
Action: Action{Type: "skip"},
|
||||
Conditions: map[string]interface{}{
|
||||
@@ -186,9 +171,9 @@ func TestValidate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "unsupported action",
|
||||
res: &ResourcePolicies{
|
||||
res: &resourcePolicies{
|
||||
Version: "v1",
|
||||
VolumePolicies: []VolumePolicy{
|
||||
VolumePolicies: []volumePolicy{
|
||||
{
|
||||
Action: Action{Type: "unsupported"},
|
||||
Conditions: map[string]interface{}{
|
||||
@@ -205,9 +190,9 @@ func TestValidate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "error format of nfs",
|
||||
res: &ResourcePolicies{
|
||||
res: &resourcePolicies{
|
||||
Version: "v1",
|
||||
VolumePolicies: []VolumePolicy{
|
||||
VolumePolicies: []volumePolicy{
|
||||
{
|
||||
Action: Action{Type: "skip"},
|
||||
Conditions: map[string]interface{}{
|
||||
@@ -221,10 +206,10 @@ func TestValidate(t *testing.T) {
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "supported format volume policies",
|
||||
res: &ResourcePolicies{
|
||||
name: "supported formart volume policies",
|
||||
res: &resourcePolicies{
|
||||
Version: "v1",
|
||||
VolumePolicies: []VolumePolicy{
|
||||
VolumePolicies: []volumePolicy{
|
||||
{
|
||||
Action: Action{Type: "skip"},
|
||||
Conditions: map[string]interface{}{
|
||||
@@ -245,86 +230,11 @@ func TestValidate(t *testing.T) {
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "supported format volume policies, action type snapshot",
|
||||
res: &ResourcePolicies{
|
||||
Version: "v1",
|
||||
VolumePolicies: []VolumePolicy{
|
||||
{
|
||||
Action: Action{Type: "snapshot"},
|
||||
Conditions: map[string]interface{}{
|
||||
"capacity": "0,10Gi",
|
||||
"storageClass": []string{"gp2", "ebs-sc"},
|
||||
"csi": interface{}(
|
||||
map[string]interface{}{
|
||||
"driver": "aws.efs.csi.driver",
|
||||
}),
|
||||
"nfs": interface{}(
|
||||
map[string]interface{}{
|
||||
"server": "192.168.20.90",
|
||||
"path": "/mnt/data/",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "supported format volume policies, action type fs-backup",
|
||||
res: &ResourcePolicies{
|
||||
Version: "v1",
|
||||
VolumePolicies: []VolumePolicy{
|
||||
{
|
||||
Action: Action{Type: "fs-backup"},
|
||||
Conditions: map[string]interface{}{
|
||||
"capacity": "0,10Gi",
|
||||
"storageClass": []string{"gp2", "ebs-sc"},
|
||||
"csi": interface{}(
|
||||
map[string]interface{}{
|
||||
"driver": "aws.efs.csi.driver",
|
||||
}),
|
||||
"nfs": interface{}(
|
||||
map[string]interface{}{
|
||||
"server": "192.168.20.90",
|
||||
"path": "/mnt/data/",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "supported format volume policies, action type fs-backup and snapshot",
|
||||
res: &ResourcePolicies{
|
||||
Version: "v1",
|
||||
VolumePolicies: []VolumePolicy{
|
||||
{
|
||||
Action: Action{Type: Snapshot},
|
||||
Conditions: map[string]interface{}{
|
||||
"storageClass": []string{"gp2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Action: Action{Type: FSBackup},
|
||||
Conditions: map[string]interface{}{
|
||||
"nfs": interface{}(
|
||||
map[string]interface{}{
|
||||
"server": "192.168.20.90",
|
||||
"path": "/mnt/data/",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
policies := &Policies{}
|
||||
err1 := policies.BuildPolicy(tc.res)
|
||||
err1 := policies.buildPolicy(tc.res)
|
||||
err2 := policies.Validate()
|
||||
|
||||
if tc.wantErr {
|
||||
|
||||
@@ -1,247 +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 resourcepolicies
|
||||
|
||||
import (
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
type volumeTypeCondition struct {
|
||||
volumeTypes []SupportedVolume
|
||||
}
|
||||
|
||||
type SupportedVolume string
|
||||
|
||||
const (
|
||||
AWSAzureDisk SupportedVolume = "awsAzureDisk"
|
||||
AWSElasticBlockStore SupportedVolume = "awsElasticBlockStore"
|
||||
AzureDisk SupportedVolume = "azureDisk"
|
||||
AzureFile SupportedVolume = "azureFile"
|
||||
Cinder SupportedVolume = "cinder"
|
||||
CephFS SupportedVolume = "cephfs"
|
||||
ConfigMap SupportedVolume = "configMap"
|
||||
CSI SupportedVolume = "csi"
|
||||
DownwardAPI SupportedVolume = "downwardAPI"
|
||||
EmptyDir SupportedVolume = "emptyDir"
|
||||
Ephemeral SupportedVolume = "ephemeral"
|
||||
FC SupportedVolume = "fc"
|
||||
Flocker SupportedVolume = "flocker"
|
||||
FlexVolume SupportedVolume = "flexVolume"
|
||||
GitRepo SupportedVolume = "gitRepo"
|
||||
Glusterfs SupportedVolume = "glusterfs"
|
||||
GCEPersistentDisk SupportedVolume = "gcePersistentDisk"
|
||||
HostPath SupportedVolume = "hostPath"
|
||||
ISCSI SupportedVolume = "iscsi"
|
||||
Local SupportedVolume = "local"
|
||||
NFS SupportedVolume = "nfs"
|
||||
PhotonPersistentDisk SupportedVolume = "photonPersistentDisk"
|
||||
PortworxVolume SupportedVolume = "portworxVolume"
|
||||
Projected SupportedVolume = "projected"
|
||||
Quobyte SupportedVolume = "quobyte"
|
||||
RBD SupportedVolume = "rbd"
|
||||
ScaleIO SupportedVolume = "scaleIO"
|
||||
Secret SupportedVolume = "secret"
|
||||
StorageOS SupportedVolume = "storageOS"
|
||||
VsphereVolume SupportedVolume = "vsphereVolume"
|
||||
)
|
||||
|
||||
func (v *volumeTypeCondition) match(s *structuredVolume) bool {
|
||||
if len(v.volumeTypes) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, vt := range v.volumeTypes {
|
||||
if vt == s.volumeType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *volumeTypeCondition) validate() error {
|
||||
// validate by yamlv3
|
||||
return nil
|
||||
}
|
||||
|
||||
func getVolumeTypeFromPV(pv *corev1api.PersistentVolume) SupportedVolume {
|
||||
if pv == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if pv.Spec.AWSElasticBlockStore != nil {
|
||||
return AWSElasticBlockStore
|
||||
}
|
||||
if pv.Spec.AzureDisk != nil {
|
||||
return AzureDisk
|
||||
}
|
||||
if pv.Spec.AzureFile != nil {
|
||||
return AzureFile
|
||||
}
|
||||
if pv.Spec.CephFS != nil {
|
||||
return CephFS
|
||||
}
|
||||
if pv.Spec.Cinder != nil {
|
||||
return Cinder
|
||||
}
|
||||
if pv.Spec.CSI != nil {
|
||||
return CSI
|
||||
}
|
||||
if pv.Spec.FC != nil {
|
||||
return FC
|
||||
}
|
||||
if pv.Spec.Flocker != nil {
|
||||
return Flocker
|
||||
}
|
||||
if pv.Spec.FlexVolume != nil {
|
||||
return FlexVolume
|
||||
}
|
||||
if pv.Spec.GCEPersistentDisk != nil {
|
||||
return GCEPersistentDisk
|
||||
}
|
||||
if pv.Spec.Glusterfs != nil {
|
||||
return Glusterfs
|
||||
}
|
||||
if pv.Spec.HostPath != nil {
|
||||
return HostPath
|
||||
}
|
||||
if pv.Spec.ISCSI != nil {
|
||||
return ISCSI
|
||||
}
|
||||
if pv.Spec.Local != nil {
|
||||
return Local
|
||||
}
|
||||
if pv.Spec.NFS != nil {
|
||||
return NFS
|
||||
}
|
||||
if pv.Spec.PhotonPersistentDisk != nil {
|
||||
return PhotonPersistentDisk
|
||||
}
|
||||
if pv.Spec.PortworxVolume != nil {
|
||||
return PortworxVolume
|
||||
}
|
||||
if pv.Spec.Quobyte != nil {
|
||||
return Quobyte
|
||||
}
|
||||
if pv.Spec.RBD != nil {
|
||||
return RBD
|
||||
}
|
||||
if pv.Spec.ScaleIO != nil {
|
||||
return ScaleIO
|
||||
}
|
||||
if pv.Spec.StorageOS != nil {
|
||||
return StorageOS
|
||||
}
|
||||
if pv.Spec.VsphereVolume != nil {
|
||||
return VsphereVolume
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getVolumeTypeFromVolume(vol *corev1api.Volume) SupportedVolume {
|
||||
if vol == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if vol.AWSElasticBlockStore != nil {
|
||||
return AWSElasticBlockStore
|
||||
}
|
||||
if vol.AzureDisk != nil {
|
||||
return AzureDisk
|
||||
}
|
||||
if vol.AzureFile != nil {
|
||||
return AzureFile
|
||||
}
|
||||
if vol.CephFS != nil {
|
||||
return CephFS
|
||||
}
|
||||
if vol.Cinder != nil {
|
||||
return Cinder
|
||||
}
|
||||
if vol.CSI != nil {
|
||||
return CSI
|
||||
}
|
||||
if vol.FC != nil {
|
||||
return FC
|
||||
}
|
||||
if vol.Flocker != nil {
|
||||
return Flocker
|
||||
}
|
||||
if vol.FlexVolume != nil {
|
||||
return FlexVolume
|
||||
}
|
||||
if vol.GCEPersistentDisk != nil {
|
||||
return GCEPersistentDisk
|
||||
}
|
||||
if vol.GitRepo != nil {
|
||||
return GitRepo
|
||||
}
|
||||
if vol.Glusterfs != nil {
|
||||
return Glusterfs
|
||||
}
|
||||
if vol.ISCSI != nil {
|
||||
return ISCSI
|
||||
}
|
||||
if vol.NFS != nil {
|
||||
return NFS
|
||||
}
|
||||
if vol.Secret != nil {
|
||||
return Secret
|
||||
}
|
||||
if vol.RBD != nil {
|
||||
return RBD
|
||||
}
|
||||
if vol.DownwardAPI != nil {
|
||||
return DownwardAPI
|
||||
}
|
||||
if vol.ConfigMap != nil {
|
||||
return ConfigMap
|
||||
}
|
||||
if vol.Projected != nil {
|
||||
return Projected
|
||||
}
|
||||
if vol.Ephemeral != nil {
|
||||
return Ephemeral
|
||||
}
|
||||
if vol.FC != nil {
|
||||
return FC
|
||||
}
|
||||
if vol.PhotonPersistentDisk != nil {
|
||||
return PhotonPersistentDisk
|
||||
}
|
||||
if vol.PortworxVolume != nil {
|
||||
return PortworxVolume
|
||||
}
|
||||
if vol.Quobyte != nil {
|
||||
return Quobyte
|
||||
}
|
||||
if vol.ScaleIO != nil {
|
||||
return ScaleIO
|
||||
}
|
||||
if vol.StorageOS != nil {
|
||||
return StorageOS
|
||||
}
|
||||
if vol.VsphereVolume != nil {
|
||||
return VsphereVolume
|
||||
}
|
||||
if vol.HostPath != nil {
|
||||
return HostPath
|
||||
}
|
||||
if vol.EmptyDir != nil {
|
||||
return EmptyDir
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user