Compare commits

...

22 Commits

Author SHA1 Message Date
Scott Seago
5fe3a50bfd Merge pull request #4030 from zubron/cherry-pick-apiservice-action-1-6-3
Cherry-pick and update changelog for v1.6.3
2021-08-11 21:43:26 -04:00
Bridget McErlean
3743ca4d53 Update changelog for v1.6.3
Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>
2021-08-11 21:04:59 -04:00
Scott Seago
6feb84a1cc Merge pull request #4028 from zubron/add-restore-item-action-to-skip-automanaged-apiservices
Skip restore of APIServices managed by Kubernetes
2021-08-11 21:03:27 -04:00
Bridget McErlean
b090b27275 Cherry-pick and update changelog for v1.6.3 (#4018)
* Use appropriate CRD API during readiness check (#4015)

* Use appropriate CRD API during readiness check

The readiness check for the Velero CRDs was still using the v1beta1 API.
This would cause the readiness check to fail on 1.22 clusters as the
v1beta1 API is no longer available. Previously, this error would be
ignored and the installation would proceed, however with #4002, we are
no longer ignoring errors from this check.

This change modifies the CRD readiness check to check the CRDs using the
same API version that was used when submitting the CRDs to the cluster.
It also introduces a new CRD builder using the V1 API for testing.

This change also fixes a bug that was identified in the polling code
where if the CRDs were not ready on the first polling iteration, they
would be added again to the list of CRDs to check resulting in
duplicates. This would cause the length check to fail on all subsequent
polls and the timeout would always be reached.

Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>

* Remove duplicate V1 CRD builder and update comment

Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>

* Merge pull request #4012 from jenting/add-k8s-1.22-ci-test

Add Kubernetes v1.22 CI test

* Update changelog for v1.6.3

Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>

Co-authored-by: Scott Seago <sseago@redhat.com>
2021-08-06 22:49:34 +08:00
Bridget McErlean
499631ba8e Cherry pick changes for 1.6.3 and add changelog (#4006)
* Merge pull request #3941 from sseago/e2e-crdversion

enable e2e tests to choose crd apiVersion

* Updated uninstall to remove both v1beta1 and v1 CRDs if present (#3997)

* Add changelog for v1.6.3

Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>

Co-authored-by: Wenkai Yin(尹文开) <yinw@vmware.com>
Co-authored-by: David L. Smith-Uchida <dsmithuchida@vmware.com>
2021-07-30 07:31:48 +08:00
Bridget McErlean
aa274e8c65 Merge pull request #3999 from ywk253100/210728_1.6_cherry_pick
[cherry-pick]Support both v1beta1 and v1 CRDs for velero
2021-07-28 20:52:15 -04:00
Wenkai Yin(尹文开)
9e7daf7e37 Fix bugs of E2E test cases
Signed-off-by: Wenkai Yin(尹文开) <yinw@vmware.com>
2021-07-29 07:37:07 +08:00
JenTing Hsiao
e2581866bc Support both v1beta1 and v1 CRDs for velero
Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>
2021-07-29 07:37:07 +08:00
Wenkai Yin(尹文开)
037e475227 Upgrade Velero ClusterRoleBinding to use v1 API (#3995)
* Upgrade Velero ClusterRoleBinding to use v1 API

Signed-off-by: JenTing Hsiao <jenting.hsiao@suse.com>

* Add the change log

Signed-off-by: Wenkai Yin(尹文开) <yinw@vmware.com>

Co-authored-by: JenTing Hsiao <jenting.hsiao@suse.com>
2021-07-27 22:38:10 -07:00
Daniel Jiang
8c9cdb9603 Merge pull request #3971 from zubron/release-1.6.2
Add cherry-pick commits and changelog for v1.6.2
2021-07-20 12:29:28 +08:00
Bridget McErlean
a77702c885 Add changelog for v1.6.2
Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>
2021-07-16 15:24:05 -04:00
Bridget McErlean
1d882c6509 Merge pull request #3928 from zubron/customize-velero-image-at-build-time
Allow image registry to be configured at build time

Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>
2021-07-16 15:23:53 -04:00
Bridget McErlean
07060d7fea Update k8s libraries to latest patch version (#3953)
Also enforce the use of the latest version of github.com/gogo/protobuf.

Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>
2021-07-16 15:10:16 -04:00
Bridget McErlean
ef34b9b654 Merge pull request #3889 from zubron/release-1.6.1
Add cherry-pick commits and changelog for v1.6.1
2021-06-22 09:15:31 -04:00
Bridget McErlean
e22d6591e4 Add changelog for v1.6.1
Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>
2021-06-21 12:01:27 -04:00
Scott Seago
38493995ad regression introduced in 1.6 restore progress: fix CR restore (#3845)
Signed-off-by: Scott Seago <sseago@redhat.com>
2021-06-21 12:01:21 -04:00
Bridget McErlean
74a0b39e3e Skip volume restores from projected sources (#3877)
In #3863, it was discovered that volumes from projected sources were
being backed up by restic when they should have been skipped. Restoring
these volumes triggers a known bug in restic.

In #3866, we started skipping volumes from a projected source, however
there will exist backups that were taken before this fix was introduced.
This change modifies the restore logic to skip the restore of any volume
that came from a projected source, allowing backups taken before #3866
to be restored successfully.

Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>
2021-06-21 12:01:21 -04:00
codegold79
a378d3a9d4 API groups e2e tests remove controllers (#3564)
* Remove controllers and sleeps in API groups e2e tests

Signed-off-by: F. Gold <fgold@vmware.com>

* Print command in AfterEach(...) and check error

Signed-off-by: F. Gold <fgold@vmware.com>

* Make change ahead of PR3764 changes in main

Signed-off-by: F. Gold <fgold@vmware.com>

* Update go.{mod,sum} files

Signed-off-by: F. Gold <fgold@vmware.com>

* Run make update

Signed-off-by: F. Gold <fgold@vmware.com>
2021-06-21 12:01:21 -04:00
Scott Seago
0f576fb748 Merge pull request #3866 from alaypatel07/fix-projected-volume-for-restic
skip backuping projected volume when using restic

Signed-off-by: Bridget McErlean <bmcerlean@vmware.com>
2021-06-21 12:00:58 -04:00
Carlisia Thompson
119529c9a2 Consolidate api clients for e2e tests (#3764)
* Consolidate api clients
* Adress Nolan reviews
* Adding back output warning for consistency
* Remove unnecessary documentation
* Address Bridget's reviews
* Update go.sum files

Signed-off-by: Carlisia <carlisia@grokkingtech.io>
Co-authored-by: Bridget McErlean <bmcerlean@vmware.com>
2021-06-21 11:17:35 -04:00
Carlisia Thompson
2c26119b10 A small refactor of the e2e tests (#3726)
* A small refactor of the e2e tests

Signed-off-by: Carlisia <carlisia@grokkingtech.io>

* Add copyright header

Signed-off-by: Carlisia <carlisia@grokkingtech.io>

* Fix CI

Signed-off-by: Carlisia <carlisia@grokkingtech.io>

* Revert unneeded changes

Signed-off-by: Carlisia <carlisia@grokkingtech.io>

* Remove file that doesnt belong here

Signed-off-by: Carlisia <carlisia@grokkingtech.io>
2021-06-21 11:17:08 -04:00
Ashish Amarnath
cbccdbd05a 🐛 Fix plugin name derivation from image name (#3711)
* 🐛 Fix plugin name derivation from image name

Signed-off-by: Ashish Amarnath <ashisham@vmware.com>

* changelog

Signed-off-by: Ashish Amarnath <ashisham@vmware.com>
2021-06-21 09:43:33 -04:00
98 changed files with 5704 additions and 32235 deletions

View File

@@ -58,6 +58,8 @@ jobs:
- 1.18.15
- 1.19.7
- 1.20.2
- 1.21.1
- 1.22.0
# 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:
@@ -75,6 +77,7 @@ jobs:
velero-${{ github.event.pull_request.number }}-
- uses: engineerd/setup-kind@v0.5.0
with:
version: "v0.11.1"
image: "kindest/node:v${{ matrix.k8s }}"
- name: Install CRDs
run: |

114
.github/workflows/e2e-test-kind.yaml vendored Normal file
View File

@@ -0,0 +1,114 @@
name: "Run the E2E test on kind"
on:
push:
pull_request:
# Do not run when the change only includes these directories.
paths-ignore:
- "site/**"
- "design/**"
jobs:
# Build the Velero CLI and image once for all Kubernetes versions, and cache it so the fan-out workers can get it.
build:
runs-on: ubuntu-latest
steps:
# Look for a CLI that's made for this PR
- name: Fetch built CLI
id: cli-cache
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@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@v2
if: steps.cli-cache.outputs.cache-hit != 'true'
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Check out the code
uses: actions/checkout@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
if: steps.cli-cache.outputs.cache-hit != 'true'
run: |
make local
# If no image were built for this PR, build it now.
- name: Build Velero Image
if: steps.image-cache.outputs.cache-hit != 'true'
run: |
IMAGE=velero VERSION=pr-test make container
docker save velero:pr-test -o ./velero.tar
# Run E2E test against all kubernetes versions on kind
run-e2e-test:
needs: build
runs-on: ubuntu-latest
strategy:
matrix:
k8s:
# doesn't cover 1.15 as 1.15 doesn't support "apiextensions.k8s.io/v1" that is needed for the case
#- 1.15.12
- 1.16.15
- 1.17.17
- 1.18.15
- 1.19.7
- 1.20.2
- 1.21.1
- 1.22.0
fail-fast: false
steps:
- name: Check out the code
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.11.1"
image: "kindest/node:v${{ matrix.k8s }}"
- name: Fetch built CLI
id: cli-cache
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@v2
with:
path: ./velero.tar
key: velero-image-${{ github.event.pull_request.number }}-${{ github.sha }}
- name: Load Velero Image
run:
kind load image-archive velero.tar
# always try to fetch the cached go modules as the e2e test needs it either
- name: Fetch cached go modules
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Run E2E test
run: |
cat << EOF > /tmp/credential
[default]
aws_access_key_id=minio
aws_secret_access_key=minio123
EOF
GOPATH=~/go CLOUD_PROVIDER=kind \
OBJECT_STORE_PROVIDER=aws BSL_CONFIG=region=minio,s3ForcePathStyle="true",s3Url=http://$(hostname -i):9000 \
CREDS_FILE=/tmp/credential BSL_BUCKET=bucket \
ADDITIONAL_OBJECT_STORE_PROVIDER=aws ADDITIONAL_BSL_CONFIG=region=minio,s3ForcePathStyle="true",s3Url=http://$(hostname -i):9000 \
ADDITIONAL_CREDS_FILE=/tmp/credential ADDITIONAL_BSL_BUCKET=additional-bucket \
GINKGO_FOCUS=Basic VELERO_IMAGE=velero:pr-test \
make -C test/e2e run

View File

@@ -14,7 +14,7 @@ jobs:
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/crds/crds.go
skip: .git,*.png,*.jpg,*.woff,*.ttf,*.gif,*.ico,./config/crd/v1beta1/crds/crds.go,./config/crd/v1/crds/crds.go
ignore_words_list: iam,aks,ist,bridget,ue
check_filenames: true
check_hidden: true

View File

@@ -41,7 +41,7 @@ builds:
- goos: windows
goarch: ppc64le
ldflags:
- -X "github.com/vmware-tanzu/velero/pkg/buildinfo.Version={{ .Tag }}" -X "github.com/vmware-tanzu/velero/pkg/buildinfo.GitSHA={{ .FullCommit }}" -X "github.com/vmware-tanzu/velero/pkg/buildinfo.GitTreeState={{ .Env.GIT_TREE_STATE }}"
- -X "github.com/vmware-tanzu/velero/pkg/buildinfo.Version={{ .Tag }}" -X "github.com/vmware-tanzu/velero/pkg/buildinfo.GitSHA={{ .FullCommit }}" -X "github.com/vmware-tanzu/velero/pkg/buildinfo.GitTreeState={{ .Env.GIT_TREE_STATE }}" -X "github.com/vmware-tanzu/velero/pkg/buildinfo.ImageRegistry={{ .Env.REGISTRY }}"
archives:
- name_template: "{{ .ProjectName }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}"
wrap_in_directory: true

View File

@@ -18,11 +18,12 @@ ARG PKG
ARG VERSION
ARG GIT_SHA
ARG GIT_TREE_STATE
ARG REGISTRY
ENV CGO_ENABLED=0 \
GO111MODULE=on \
GOPROXY=${GOPROXY} \
LDFLAGS="-X ${PKG}/pkg/buildinfo.Version=${VERSION} -X ${PKG}/pkg/buildinfo.GitSHA=${GIT_SHA} -X ${PKG}/pkg/buildinfo.GitTreeState=${GIT_TREE_STATE}"
LDFLAGS="-X ${PKG}/pkg/buildinfo.Version=${VERSION} -X ${PKG}/pkg/buildinfo.GitSHA=${GIT_SHA} -X ${PKG}/pkg/buildinfo.GitTreeState=${GIT_TREE_STATE} -X ${PKG}/pkg/buildinfo.ImageRegistry=${REGISTRY}"
WORKDIR /go/src/github.com/vmware-tanzu/velero

View File

@@ -110,7 +110,6 @@ GOPROXY ?= https://proxy.golang.org
# If you want to build all binaries, see the 'all-build' rule.
# If you want to build all containers, see the 'all-containers' rule.
# If you want to build AND push all containers, see the 'all-push' rule.
all:
@$(MAKE) build
@$(MAKE) build BIN=velero-restic-restore-helper
@@ -129,6 +128,7 @@ local: build-dirs
GOOS=$(GOOS) \
GOARCH=$(GOARCH) \
VERSION=$(VERSION) \
REGISTRY=$(REGISTRY) \
PKG=$(PKG) \
BIN=$(BIN) \
GIT_SHA=$(GIT_SHA) \
@@ -144,6 +144,7 @@ _output/bin/$(GOOS)/$(GOARCH)/$(BIN): build-dirs
GOOS=$(GOOS) \
GOARCH=$(GOARCH) \
VERSION=$(VERSION) \
REGISTRY=$(REGISTRY) \
PKG=$(PKG) \
BIN=$(BIN) \
GIT_SHA=$(GIT_SHA) \
@@ -186,6 +187,7 @@ endif
--build-arg=VERSION=$(VERSION) \
--build-arg=GIT_SHA=$(GIT_SHA) \
--build-arg=GIT_TREE_STATE=$(GIT_TREE_STATE) \
--build-arg=REGISTRY=$(REGISTRY) \
-f $(VELERO_DOCKERFILE) .
container:
@@ -201,6 +203,7 @@ endif
--build-arg=VERSION=$(VERSION) \
--build-arg=GIT_SHA=$(GIT_SHA) \
--build-arg=GIT_TREE_STATE=$(GIT_TREE_STATE) \
--build-arg=REGISTRY=$(REGISTRY) \
--build-arg=RESTIC_VERSION=$(RESTIC_VERSION) \
-f $(VELERO_DOCKERFILE) .
@echo "container: $(IMAGE):$(VERSION)"
@@ -346,6 +349,7 @@ release:
GITHUB_TOKEN=$(GITHUB_TOKEN) \
RELEASE_NOTES_FILE=$(RELEASE_NOTES_FILE) \
PUBLISH=$(PUBLISH) \
REGISTRY=$(REGISTRY) \
./hack/release-tools/goreleaser.sh'"
serve-docs: build-image-hugo

View File

@@ -1,17 +1,17 @@
# -*- mode: Python -*-
k8s_yaml([
'config/crd/bases/velero.io_backups.yaml',
'config/crd/bases/velero.io_backupstoragelocations.yaml',
'config/crd/bases/velero.io_deletebackuprequests.yaml',
'config/crd/bases/velero.io_downloadrequests.yaml',
'config/crd/bases/velero.io_podvolumebackups.yaml',
'config/crd/bases/velero.io_podvolumerestores.yaml',
'config/crd/bases/velero.io_resticrepositories.yaml',
'config/crd/bases/velero.io_restores.yaml',
'config/crd/bases/velero.io_schedules.yaml',
'config/crd/bases/velero.io_serverstatusrequests.yaml',
'config/crd/bases/velero.io_volumesnapshotlocations.yaml',
'config/crd/v1/bases/velero.io_backups.yaml',
'config/crd/v1/bases/velero.io_backupstoragelocations.yaml',
'config/crd/v1/bases/velero.io_deletebackuprequests.yaml',
'config/crd/v1/bases/velero.io_downloadrequests.yaml',
'config/crd/v1/bases/velero.io_podvolumebackups.yaml',
'config/crd/v1/bases/velero.io_podvolumerestores.yaml',
'config/crd/v1/bases/velero.io_resticrepositories.yaml',
'config/crd/v1/bases/velero.io_restores.yaml',
'config/crd/v1/bases/velero.io_schedules.yaml',
'config/crd/v1/bases/velero.io_serverstatusrequests.yaml',
'config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml',
])
# default values

View File

@@ -1,3 +1,80 @@
## v1.6.3
### 2021-07-30
### Download
https://github.com/vmware-tanzu/velero/releases/tag/v1.6.3
### Container Image
`velero/velero:v1.6.3`
### Documentation
https://velero.io/docs/v1.6/
### Upgrading
https://velero.io/docs/v1.6/upgrade-to-1.6/
### Highlights
This release introduces changes to provide compatibility with Kubernetes v1.22.
The `apiextensions.k8s.io/v1beta1` API version of `CustomResourceDefinition` will no longer be served in Kubernetes v1.22.
Velero will now use the cluster preferred API version for the `CustomResourceDefinition`s that it creates.
If you are using Kubernetes v1.15 or earlier, the `apiextensions.k8s.io/v1beta1` API version will be used.
If you are using Kubernetes v1.22 or later, the `apiextensions.k8s.io/v1` API version will be used.
For clusters between these versions, the cluster preferred API version will be used.
The `rbac.authorization.k8s.io/v1beta1` API version of `ClusterRoleBinding` will no longer be served in Kubernetes v1.22.
Velero will now use the `rbac.authorization.k8s.io/v1` API version for the `ClusterRoleBinding`s that it creates.
This API version was introduced in Kubernetes v1.8.
### All Changes
* enable e2e tests to choose crd apiVersion (#3941, @sseago)
* Upgrade Velero ClusterRoleBinding to use v1 API (#3995, @jenting)
* Install Kubernetes preferred CRDs API version (v1beta1/v1). (#3999, @jenting)
* Use the cluster preferred CRD API version when polling for Velero CRD readiness. (#4015, @zubron)
* Add a RestoreItemAction plugin (`velero.io/apiservice`) which skips the restore of any `APIService` which is managed by Kubernetes. These are identified using the `kube-aggregator.kubernetes.io/automanaged` label. (#4028, @zubron)
## v1.6.2
### 2021-07-16
### Download
https://github.com/vmware-tanzu/velero/releases/tag/v1.6.2
### Container Image
`velero/velero:v1.6.2`
### Documentation
https://velero.io/docs/v1.6/
### Upgrading
https://velero.io/docs/v1.6/upgrade-to-1.6/
This release contains no user facing changes but includes fixes for CVE-2021-3121 and CVE-2021-3580.
## v1.6.1
### 2021-06-21
### Download
https://github.com/vmware-tanzu/velero/releases/tag/v1.6.1
### Container Image
`velero/velero:v1.6.1`
### Documentation
https://velero.io/docs/v1.6/
### Upgrading
https://velero.io/docs/v1.6/upgrade-to-1.6/
### All Changes
* Fix CR restore regression introduced in 1.6 restore progress. (#3845, @sseago)
* Skip the restore of volumes that originally came from a projected volume when using restic. (#3877, @zubron)
* skip backuping projected volume when using restic (#3866, @alaypatel07)
* 🐛 Fix plugin name derivation from image name (#3711, @ashish-amarnath)
## v1.6.0
### 2021-04-12

View File

@@ -0,0 +1,433 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: backups.velero.io
spec:
group: velero.io
names:
kind: Backup
listKind: BackupList
plural: backups
singular: backup
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
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'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: BackupSpec defines the specification for a Velero backup.
properties:
defaultVolumesToRestic:
description: DefaultVolumesToRestic specifies whether restic should
be used to take a backup of all pod volumes by default.
type: boolean
excludedNamespaces:
description: ExcludedNamespaces contains a list of namespaces that
are not included in the backup.
items:
type: string
nullable: true
type: array
excludedResources:
description: ExcludedResources is a slice of resource names that are
not included in the backup.
items:
type: string
nullable: true
type: array
hooks:
description: Hooks represent custom behaviors that should be executed
at different phases of the backup.
properties:
resources:
description: Resources are hooks that should be executed when
backing up individual instances of a resource.
items:
description: BackupResourceHookSpec defines one or more BackupResourceHooks
that should be executed based on the rules defined for namespaces,
resources, and label selector.
properties:
excludedNamespaces:
description: ExcludedNamespaces specifies the namespaces
to which this hook spec does not apply.
items:
type: string
nullable: true
type: array
excludedResources:
description: ExcludedResources specifies the resources to
which this hook spec does not apply.
items:
type: string
nullable: true
type: array
includedNamespaces:
description: IncludedNamespaces specifies the namespaces
to which this hook spec applies. If empty, it applies
to all namespaces.
items:
type: string
nullable: true
type: array
includedResources:
description: IncludedResources specifies the resources to
which this hook spec applies. If empty, it applies to
all resources.
items:
type: string
nullable: true
type: array
labelSelector:
description: LabelSelector, if specified, filters the resources
to which this hook spec applies.
nullable: true
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
name:
description: Name is the name of this hook.
type: string
post:
description: PostHooks is a list of BackupResourceHooks
to execute after storing the item in the backup. These
are executed after all "additional items" from item actions
are processed.
items:
description: BackupResourceHook defines a hook for a resource.
properties:
exec:
description: Exec defines an exec hook.
properties:
command:
description: Command is the command and arguments
to execute.
items:
type: string
minItems: 1
type: array
container:
description: Container is the container in the
pod where the command should be executed. If
not specified, the pod's first container is
used.
type: string
onError:
description: OnError specifies how Velero should
behave if it encounters an error executing this
hook.
enum:
- Continue
- Fail
type: string
timeout:
description: Timeout defines the maximum amount
of time Velero should wait for the hook to complete
before considering the execution a failure.
type: string
required:
- command
type: object
required:
- exec
type: object
type: array
pre:
description: PreHooks is a list of BackupResourceHooks to
execute prior to storing the item in the backup. These
are executed before any "additional items" from item actions
are processed.
items:
description: BackupResourceHook defines a hook for a resource.
properties:
exec:
description: Exec defines an exec hook.
properties:
command:
description: Command is the command and arguments
to execute.
items:
type: string
minItems: 1
type: array
container:
description: Container is the container in the
pod where the command should be executed. If
not specified, the pod's first container is
used.
type: string
onError:
description: OnError specifies how Velero should
behave if it encounters an error executing this
hook.
enum:
- Continue
- Fail
type: string
timeout:
description: Timeout defines the maximum amount
of time Velero should wait for the hook to complete
before considering the execution a failure.
type: string
required:
- command
type: object
required:
- exec
type: object
type: array
required:
- name
type: object
nullable: true
type: array
type: object
includeClusterResources:
description: IncludeClusterResources specifies whether cluster-scoped
resources should be included for consideration in the backup.
nullable: true
type: boolean
includedNamespaces:
description: IncludedNamespaces is a slice of namespace names to include
objects from. If empty, all namespaces are included.
items:
type: string
nullable: true
type: array
includedResources:
description: IncludedResources is a slice of resource names to include
in the backup. If empty, all resources are included.
items:
type: string
nullable: true
type: array
labelSelector:
description: LabelSelector is a metav1.LabelSelector to filter with
when adding individual objects to the backup. If empty or nil, all
objects are included. Optional.
nullable: true
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: A label selector requirement is a selector that
contains values, a key, and an operator that relates the key
and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: operator represents a key's relationship to
a set of values. Valid operators are In, NotIn, Exists
and DoesNotExist.
type: string
values:
description: values is an array of string values. If the
operator is In or NotIn, the values array must be non-empty.
If the operator is Exists or DoesNotExist, the values
array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs. A single
{key,value} in the matchLabels map is equivalent to an element
of matchExpressions, whose key field is "key", the operator
is "In", and the values array contains only "value". The requirements
are ANDed.
type: object
type: object
orderedResources:
additionalProperties:
type: string
description: OrderedResources specifies the backup order of resources
of specific Kind. The map key is the Kind name and value is a list
of resource names separated by commas. Each resource name has format
"namespace/resourcename". For cluster resources, simply use "resourcename".
nullable: true
type: object
snapshotVolumes:
description: SnapshotVolumes specifies whether to take cloud snapshots
of any PV's referenced in the set of objects included in the Backup.
nullable: true
type: boolean
storageLocation:
description: StorageLocation is a string containing the name of a
BackupStorageLocation where the backup should be stored.
type: string
ttl:
description: TTL is a time.Duration-parseable string describing how
long the Backup should be retained for.
type: string
volumeSnapshotLocations:
description: VolumeSnapshotLocations is a list containing names of
VolumeSnapshotLocations associated with this backup.
items:
type: string
type: array
type: object
status:
description: BackupStatus captures the current status of a Velero backup.
properties:
completionTimestamp:
description: CompletionTimestamp records the time a backup was completed.
Completion time is recorded even on failed backups. Completion time
is recorded before uploading the backup object. The server's time
is used for CompletionTimestamps
format: date-time
nullable: true
type: string
errors:
description: Errors is a count of all error messages that were generated
during execution of the backup. The actual errors are in the backup's
log file in object storage.
type: integer
expiration:
description: Expiration is when this Backup is eligible for garbage-collection.
format: date-time
nullable: true
type: string
formatVersion:
description: FormatVersion is the backup format version, including
major, minor, and patch version.
type: string
phase:
description: Phase is the current state of the Backup.
enum:
- New
- FailedValidation
- InProgress
- Completed
- PartiallyFailed
- Failed
- Deleting
type: string
progress:
description: Progress contains information about the backup's execution
progress. Note that this information is best-effort only -- if Velero
fails to update it during a backup for any reason, it may be inaccurate/stale.
nullable: true
properties:
itemsBackedUp:
description: ItemsBackedUp is the number of items that have actually
been written to the backup tarball so far.
type: integer
totalItems:
description: TotalItems is the total number of items to be backed
up. This number may change throughout the execution of the backup
due to plugins that return additional related items to back
up, the velero.io/exclude-from-backup label, and various other
filters that happen as items are processed.
type: integer
type: object
startTimestamp:
description: StartTimestamp records the time a backup was started.
Separate from CreationTimestamp, since that value changes on restores.
The server's time is used for StartTimestamps
format: date-time
nullable: true
type: string
validationErrors:
description: ValidationErrors is a slice of all validation errors
(if applicable).
items:
type: string
nullable: true
type: array
version:
description: 'Version is the backup format major version. Deprecated:
Please see FormatVersion'
type: integer
volumeSnapshotsAttempted:
description: VolumeSnapshotsAttempted is the total number of attempted
volume snapshots for this backup.
type: integer
volumeSnapshotsCompleted:
description: VolumeSnapshotsCompleted is the total number of successfully
completed volume snapshots for this backup.
type: integer
warnings:
description: Warnings is a count of all warning messages that were
generated during execution of the backup. The actual warnings are
in the backup's log file in object storage.
type: integer
type: object
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,178 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: backupstoragelocations.velero.io
spec:
group: velero.io
names:
kind: BackupStorageLocation
listKind: BackupStorageLocationList
plural: backupstoragelocations
shortNames:
- bsl
singular: backupstoragelocation
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Backup Storage Location status such as Available/Unavailable
jsonPath: .status.phase
name: Phase
type: string
- description: LastValidationTime is the last time the backup store location was
validated
jsonPath: .status.lastValidationTime
name: Last Validated
type: date
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
- description: Default backup storage location
jsonPath: .spec.default
name: Default
type: boolean
name: v1
schema:
openAPIV3Schema:
description: BackupStorageLocation is a location where Velero stores backup
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'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: BackupStorageLocationSpec defines the desired state of a
Velero BackupStorageLocation
properties:
accessMode:
description: AccessMode defines the permissions for the backup storage
location.
enum:
- ReadOnly
- ReadWrite
type: string
backupSyncPeriod:
description: BackupSyncPeriod defines how frequently to sync backup
API objects from object storage. A value of 0 disables sync.
nullable: true
type: string
config:
additionalProperties:
type: string
description: Config is for provider-specific configuration fields.
type: object
credential:
description: Credential contains the credential information intended
to be used with this location
properties:
key:
description: The key of the secret to select from. Must be a
valid secret key.
type: string
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
optional:
description: Specify whether the Secret or its key must be defined
type: boolean
required:
- key
type: object
default:
description: Default indicates this location is the default backup
storage location.
type: boolean
objectStorage:
description: ObjectStorageLocation specifies the settings necessary
to connect to a provider's object storage.
properties:
bucket:
description: Bucket is the bucket to use for object storage.
type: string
caCert:
description: CACert defines a CA bundle to use when verifying
TLS connections to the provider.
format: byte
type: string
prefix:
description: Prefix is the path inside a bucket to use for Velero
storage. Optional.
type: string
required:
- bucket
type: object
provider:
description: Provider is the provider of the backup storage.
type: string
validationFrequency:
description: ValidationFrequency defines how frequently to validate
the corresponding object storage. A value of 0 disables validation.
nullable: true
type: string
required:
- objectStorage
- provider
type: object
status:
description: BackupStorageLocationStatus defines the observed state of
BackupStorageLocation
properties:
accessMode:
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. \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.
format: date-time
nullable: true
type: string
lastValidationTime:
description: LastValidationTime is the last time the backup store
location was validated the cluster.
format: date-time
nullable: true
type: string
phase:
description: Phase is the current state of the BackupStorageLocation.
enum:
- Available
- Unavailable
type: string
type: object
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,71 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: deletebackuprequests.velero.io
spec:
group: velero.io
names:
kind: DeleteBackupRequest
listKind: DeleteBackupRequestList
plural: deletebackuprequests
singular: deletebackuprequest
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
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'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: DeleteBackupRequestSpec is the specification for which backups
to delete.
properties:
backupName:
type: string
required:
- backupName
type: object
status:
description: DeleteBackupRequestStatus is the current status of a DeleteBackupRequest.
properties:
errors:
description: Errors contains any errors that were encountered during
the deletion process.
items:
type: string
nullable: true
type: array
phase:
description: Phase is the current state of the DeleteBackupRequest.
enum:
- New
- InProgress
- Processed
type: string
type: object
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,94 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: downloadrequests.velero.io
spec:
group: velero.io
names:
kind: DownloadRequest
listKind: DownloadRequestList
plural: downloadrequests
singular: downloadrequest
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
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'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: DownloadRequestSpec is the specification for a download request.
properties:
target:
description: Target is what to download (e.g. logs for a backup).
properties:
kind:
description: Kind is the type of file to download.
enum:
- BackupLog
- BackupContents
- BackupVolumeSnapshots
- BackupResourceList
- RestoreLog
- RestoreResults
type: string
name:
description: Name is the name of the kubernetes resource with
which the file is associated.
type: string
required:
- kind
- name
type: object
required:
- target
type: object
status:
description: DownloadRequestStatus is the current status of a DownloadRequest.
properties:
downloadURL:
description: DownloadURL contains the pre-signed URL for the target
file.
type: string
expiration:
description: Expiration is when this DownloadRequest expires and can
be deleted by the system.
format: date-time
nullable: true
type: string
phase:
description: Phase is the current state of the DownloadRequest.
enum:
- New
- Processed
type: string
type: object
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,161 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: podvolumebackups.velero.io
spec:
group: velero.io
names:
kind: PodVolumeBackup
listKind: PodVolumeBackupList
plural: podvolumebackups
singular: podvolumebackup
scope: Namespaced
versions:
- name: v1
schema:
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'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: PodVolumeBackupSpec is the specification for a PodVolumeBackup.
properties:
backupStorageLocation:
description: BackupStorageLocation is the name of the backup storage
location where the restic repository is stored.
type: string
node:
description: Node is the name of the node that the Pod is running
on.
type: string
pod:
description: Pod is a reference to the pod containing the volume to
be backed up.
properties:
apiVersion:
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.'
type: string
kind:
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'
type: string
namespace:
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'
type: string
uid:
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
type: object
repoIdentifier:
description: RepoIdentifier is the restic repository identifier.
type: string
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
volume:
description: Volume is the name of the volume within the Pod to be
backed up.
type: string
required:
- backupStorageLocation
- node
- pod
- repoIdentifier
- volume
type: object
status:
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
format: date-time
nullable: true
type: string
message:
description: Message is a message about the pod volume backup's status.
type: string
path:
description: Path is the full path within the controller pod being
backed up.
type: string
phase:
description: Phase is the current state of the PodVolumeBackup.
enum:
- New
- InProgress
- Completed
- 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.
properties:
bytesDone:
format: int64
type: integer
totalBytes:
format: int64
type: integer
type: object
snapshotID:
description: SnapshotID is the identifier for the snapshot of the
pod volume.
type: string
startTimestamp:
description: StartTimestamp records the time a backup was started.
Separate from CreationTimestamp, since that value changes on restores.
The server's time is used for StartTimestamps
format: date-time
nullable: true
type: string
type: object
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,144 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: podvolumerestores.velero.io
spec:
group: velero.io
names:
kind: PodVolumeRestore
listKind: PodVolumeRestoreList
plural: podvolumerestores
singular: podvolumerestore
scope: Namespaced
versions:
- name: v1
schema:
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'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: PodVolumeRestoreSpec is the specification for a PodVolumeRestore.
properties:
backupStorageLocation:
description: BackupStorageLocation is the name of the backup storage
location where the restic repository is stored.
type: string
pod:
description: Pod is a reference to the pod containing the volume to
be restored.
properties:
apiVersion:
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.'
type: string
kind:
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'
type: string
namespace:
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'
type: string
uid:
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
type: object
repoIdentifier:
description: RepoIdentifier is the restic repository identifier.
type: string
snapshotID:
description: SnapshotID is the ID of the volume snapshot to be restored.
type: string
volume:
description: Volume is the name of the volume within the Pod to be
restored.
type: string
required:
- backupStorageLocation
- pod
- repoIdentifier
- snapshotID
- volume
type: object
status:
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
format: date-time
nullable: true
type: string
message:
description: Message is a message about the pod volume restore's status.
type: string
phase:
description: Phase is the current state of the PodVolumeRestore.
enum:
- New
- InProgress
- Completed
- 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.
properties:
bytesDone:
format: int64
type: integer
totalBytes:
format: int64
type: integer
type: object
startTimestamp:
description: StartTimestamp records the time a restore was started.
The server's time is used for StartTimestamps
format: date-time
nullable: true
type: string
type: object
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,89 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: resticrepositories.velero.io
spec:
group: velero.io
names:
kind: ResticRepository
listKind: ResticRepositoryList
plural: resticrepositories
singular: resticrepository
scope: Namespaced
versions:
- name: v1
schema:
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'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: ResticRepositorySpec is the specification for a ResticRepository.
properties:
backupStorageLocation:
description: BackupStorageLocation is the name of the BackupStorageLocation
that should contain this repository.
type: string
maintenanceFrequency:
description: MaintenanceFrequency is how often maintenance should
be run.
type: string
resticIdentifier:
description: ResticIdentifier is the full restic-compatible string
for identifying this repository.
type: string
volumeNamespace:
description: VolumeNamespace is the namespace this restic repository
contains pod volume backups for.
type: string
required:
- backupStorageLocation
- maintenanceFrequency
- resticIdentifier
- volumeNamespace
type: object
status:
description: ResticRepositoryStatus is the current status of a ResticRepository.
properties:
lastMaintenanceTime:
description: LastMaintenanceTime is the last time maintenance was
run.
format: date-time
nullable: true
type: string
message:
description: Message is a message about the current status of the
ResticRepository.
type: string
phase:
description: Phase is the current state of the ResticRepository.
enum:
- New
- Ready
- NotReady
type: string
type: object
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,394 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: schedules.velero.io
spec:
group: velero.io
names:
kind: Schedule
listKind: ScheduleList
plural: schedules
singular: schedule
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: Schedule is a Velero resource that represents a pre-scheduled
or periodic Backup that should be run.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: ScheduleSpec defines the specification for a Velero schedule
properties:
schedule:
description: Schedule is a Cron expression defining when to run the
Backup.
type: string
template:
description: Template is the definition of the Backup to be run on
the provided schedule
properties:
defaultVolumesToRestic:
description: DefaultVolumesToRestic specifies whether restic should
be used to take a backup of all pod volumes by default.
type: boolean
excludedNamespaces:
description: ExcludedNamespaces contains a list of namespaces
that are not included in the backup.
items:
type: string
nullable: true
type: array
excludedResources:
description: ExcludedResources is a slice of resource names that
are not included in the backup.
items:
type: string
nullable: true
type: array
hooks:
description: Hooks represent custom behaviors that should be executed
at different phases of the backup.
properties:
resources:
description: Resources are hooks that should be executed when
backing up individual instances of a resource.
items:
description: BackupResourceHookSpec defines one or more
BackupResourceHooks that should be executed based on the
rules defined for namespaces, resources, and label selector.
properties:
excludedNamespaces:
description: ExcludedNamespaces specifies the namespaces
to which this hook spec does not apply.
items:
type: string
nullable: true
type: array
excludedResources:
description: ExcludedResources specifies the resources
to which this hook spec does not apply.
items:
type: string
nullable: true
type: array
includedNamespaces:
description: IncludedNamespaces specifies the namespaces
to which this hook spec applies. If empty, it applies
to all namespaces.
items:
type: string
nullable: true
type: array
includedResources:
description: IncludedResources specifies the resources
to which this hook spec applies. If empty, it applies
to all resources.
items:
type: string
nullable: true
type: array
labelSelector:
description: LabelSelector, if specified, filters the
resources to which this hook spec applies.
nullable: true
properties:
matchExpressions:
description: matchExpressions is a list of label
selector requirements. The requirements are ANDed.
items:
description: A label selector requirement is a
selector that contains values, a key, and an
operator that relates the key and values.
properties:
key:
description: key is the label key that the
selector applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are
In, NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string
values. If the operator is In or NotIn,
the values array must be non-empty. If the
operator is Exists or DoesNotExist, the
values array must be empty. This array is
replaced during a strategic merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value}
pairs. A single {key,value} in the matchLabels
map is equivalent to an element of matchExpressions,
whose key field is "key", the operator is "In",
and the values array contains only "value". The
requirements are ANDed.
type: object
type: object
name:
description: Name is the name of this hook.
type: string
post:
description: PostHooks is a list of BackupResourceHooks
to execute after storing the item in the backup. These
are executed after all "additional items" from item
actions are processed.
items:
description: BackupResourceHook defines a hook for
a resource.
properties:
exec:
description: Exec defines an exec hook.
properties:
command:
description: Command is the command and arguments
to execute.
items:
type: string
minItems: 1
type: array
container:
description: Container is the container in
the pod where the command should be executed.
If not specified, the pod's first container
is used.
type: string
onError:
description: OnError specifies how Velero
should behave if it encounters an error
executing this hook.
enum:
- Continue
- Fail
type: string
timeout:
description: Timeout defines the maximum amount
of time Velero should wait for the hook
to complete before considering the execution
a failure.
type: string
required:
- command
type: object
required:
- exec
type: object
type: array
pre:
description: PreHooks is a list of BackupResourceHooks
to execute prior to storing the item in the backup.
These are executed before any "additional items" from
item actions are processed.
items:
description: BackupResourceHook defines a hook for
a resource.
properties:
exec:
description: Exec defines an exec hook.
properties:
command:
description: Command is the command and arguments
to execute.
items:
type: string
minItems: 1
type: array
container:
description: Container is the container in
the pod where the command should be executed.
If not specified, the pod's first container
is used.
type: string
onError:
description: OnError specifies how Velero
should behave if it encounters an error
executing this hook.
enum:
- Continue
- Fail
type: string
timeout:
description: Timeout defines the maximum amount
of time Velero should wait for the hook
to complete before considering the execution
a failure.
type: string
required:
- command
type: object
required:
- exec
type: object
type: array
required:
- name
type: object
nullable: true
type: array
type: object
includeClusterResources:
description: IncludeClusterResources specifies whether cluster-scoped
resources should be included for consideration in the backup.
nullable: true
type: boolean
includedNamespaces:
description: IncludedNamespaces is a slice of namespace names
to include objects from. If empty, all namespaces are included.
items:
type: string
nullable: true
type: array
includedResources:
description: IncludedResources is a slice of resource names to
include in the backup. If empty, all resources are included.
items:
type: string
nullable: true
type: array
labelSelector:
description: LabelSelector is a metav1.LabelSelector to filter
with when adding individual objects to the backup. If empty
or nil, all objects are included. Optional.
nullable: true
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that relates
the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In, NotIn,
Exists and DoesNotExist.
type: string
values:
description: values is an array of string values. If
the operator is In or NotIn, the values array must
be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. This array is replaced
during a strategic merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs. A
single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field is "key",
the operator is "In", and the values array contains only
"value". The requirements are ANDed.
type: object
type: object
orderedResources:
additionalProperties:
type: string
description: OrderedResources specifies the backup order of resources
of specific Kind. The map key is the Kind name and value is
a list of resource names separated by commas. Each resource
name has format "namespace/resourcename". For cluster resources,
simply use "resourcename".
nullable: true
type: object
snapshotVolumes:
description: SnapshotVolumes specifies whether to take cloud snapshots
of any PV's referenced in the set of objects included in the
Backup.
nullable: true
type: boolean
storageLocation:
description: StorageLocation is a string containing the name of
a BackupStorageLocation where the backup should be stored.
type: string
ttl:
description: TTL is a time.Duration-parseable string describing
how long the Backup should be retained for.
type: string
volumeSnapshotLocations:
description: VolumeSnapshotLocations is a list containing names
of VolumeSnapshotLocations associated with this backup.
items:
type: string
type: array
type: object
useOwnerReferencesInBackup:
description: UseOwnerReferencesBackup specifies whether to use OwnerReferences
on backups created by this Schedule.
nullable: true
type: boolean
required:
- schedule
- template
type: object
status:
description: ScheduleStatus captures the current state of a Velero schedule
properties:
lastBackup:
description: LastBackup is the last time a Backup was run for this
Schedule schedule
format: date-time
nullable: true
type: string
phase:
description: Phase is the current phase of the Schedule
enum:
- New
- Enabled
- FailedValidation
type: string
validationErrors:
description: ValidationErrors is a slice of all validation errors
(if applicable)
items:
type: string
type: array
type: object
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,87 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: serverstatusrequests.velero.io
spec:
group: velero.io
names:
kind: ServerStatusRequest
listKind: ServerStatusRequestList
plural: serverstatusrequests
shortNames:
- ssr
singular: serverstatusrequest
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
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'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: ServerStatusRequestSpec is the specification for a ServerStatusRequest.
type: object
status:
description: ServerStatusRequestStatus is the current status of a ServerStatusRequest.
properties:
phase:
description: Phase is the current lifecycle phase of the ServerStatusRequest.
enum:
- New
- Processed
type: string
plugins:
description: Plugins list information about the plugins running on
the Velero server
items:
description: PluginInfo contains attributes of a Velero plugin
properties:
kind:
type: string
name:
type: string
required:
- kind
- name
type: object
nullable: true
type: array
processedTimestamp:
description: ProcessedTimestamp is when the ServerStatusRequest was
processed by the ServerStatusRequestController.
format: date-time
nullable: true
type: string
serverVersion:
description: ServerVersion is the Velero server version.
type: string
type: object
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,72 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.3.0
creationTimestamp: null
name: volumesnapshotlocations.velero.io
spec:
group: velero.io
names:
kind: VolumeSnapshotLocation
listKind: VolumeSnapshotLocationList
plural: volumesnapshotlocations
singular: volumesnapshotlocation
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: VolumeSnapshotLocation is a location where Velero stores volume
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'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: VolumeSnapshotLocationSpec defines the specification for
a Velero VolumeSnapshotLocation.
properties:
config:
additionalProperties:
type: string
description: Config is for provider-specific configuration fields.
type: object
provider:
description: Provider is the provider of the volume storage.
type: string
required:
- provider
type: object
status:
description: VolumeSnapshotLocationStatus describes the current status
of a Velero VolumeSnapshotLocation.
properties:
phase:
description: VolumeSnapshotLocationPhase is the lifecycle phase of
a Velero VolumeSnapshotLocation.
enum:
- Available
- Unavailable
type: string
type: object
type: object
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
// Package crds embeds the controller-tools generated CRD manifests
package crds
//go:generate go run ../../../hack/crd-gen/main.go
//go:generate go run ../../../../hack/crd-gen/v1/main.go

View File

@@ -0,0 +1,4 @@
// Package crds embeds the controller-tools generated CRD manifests
package crds
//go:generate go run ../../../../hack/crd-gen/v1beta1/main.go

17
go.mod
View File

@@ -20,7 +20,7 @@ require (
github.com/hashicorp/go-plugin v0.0.0-20190610192547-a1bc61569a26
github.com/joho/godotenv v1.3.0
github.com/kubernetes-csi/external-snapshotter/client/v4 v4.0.0
github.com/onsi/ginkgo v1.15.2
github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.10.2
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.7.1
@@ -35,12 +35,17 @@ require (
google.golang.org/genproto v0.0.0-20200731012542-8145dea6a485 // indirect
google.golang.org/grpc v1.31.0
google.golang.org/protobuf v1.25.0 // indirect
k8s.io/api v0.19.7
k8s.io/apiextensions-apiserver v0.19.7
k8s.io/apimachinery v0.19.7
k8s.io/cli-runtime v0.19.7
k8s.io/client-go v0.19.7
k8s.io/api v0.19.12
k8s.io/apiextensions-apiserver v0.19.12
k8s.io/apimachinery v0.19.12
k8s.io/cli-runtime v0.19.12
k8s.io/client-go v0.19.12
k8s.io/klog v1.0.0
k8s.io/klog/v2 v2.3.0 // indirect
k8s.io/kube-aggregator v0.19.12
k8s.io/utils v0.0.0-20201005171033-6301aaf42dc7 // indirect
sigs.k8s.io/cluster-api v0.3.11-0.20210106212952-b6c1b5b3db3d
sigs.k8s.io/controller-runtime v0.7.1-0.20201215171748-096b2e07c091
)
replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2

67
go.sum
View File

@@ -214,16 +214,15 @@ github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gobuffalo/flect v0.2.2 h1:PAVD7sp0KOdfswjAw9BpLCU9hXo7wFSzgpQ+zNeks/A=
github.com/gobuffalo/flect v0.2.2/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -344,8 +343,7 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -417,7 +415,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@@ -431,8 +428,8 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.15.2 h1:l77YT15o814C2qVL47NOyjV/6RbaP7kKdrvZnxQ3Org=
github.com/onsi/ginkgo v1.15.2/go.mod h1:Dd6YFfwBW84ETqqtL0CPyPXillHgY6XhQH3uuCCTr/o=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@@ -628,7 +625,6 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
@@ -685,7 +681,6 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -702,10 +697,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -732,13 +725,13 @@ golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8=
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -819,7 +812,6 @@ gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -830,34 +822,34 @@ honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXe
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw=
k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI=
k8s.io/api v0.19.7 h1:MpHhls03C2pyzoYcpbe4QqYiiZjdvW+tuWq6TbjV14Y=
k8s.io/api v0.19.7/go.mod h1:KTryDUT3l6Mtv7K2J2486PNL9DBns3wOYTkGR+iz63Y=
k8s.io/api v0.19.12 h1:412/DHd7Vofk2RMb0+RHWYYZwmEOG5Kh54+T/kyi4Zg=
k8s.io/api v0.19.12/go.mod h1:EK+KvSq2urA6+CjVdZyAHEphXoLq2K2eW6lxOzTKSaY=
k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg=
k8s.io/apiextensions-apiserver v0.19.7 h1:aV9DANMSCCYBEMbtoT/5oesrtcciQrjy9yqWVtZZL5A=
k8s.io/apiextensions-apiserver v0.19.7/go.mod h1:XJNNtjISNNePDEUClHt/igzMpQcmjVVh88QH+PKztPU=
k8s.io/apiextensions-apiserver v0.19.12 h1:wY1liMH0Q2XozYj/qFQp+MJR0BbuMBn/L8/IMDvDByo=
k8s.io/apiextensions-apiserver v0.19.12/go.mod h1:LXHRkG+7v33P+IzTKYVuqffGlB7Xg7K0VgfObTacbQg=
k8s.io/apimachinery v0.18.8/go.mod h1:6sQd+iHEqmOtALqOFjSWp2KZ9F0wlU/nWm0ZgsYWMig=
k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
k8s.io/apimachinery v0.19.7 h1:nTaEnYVH+i//aPgMA0zTEV2lfVLCV9LextqVd67mulc=
k8s.io/apimachinery v0.19.7/go.mod h1:6sRbGRAVY5DOCuZwB5XkqguBqpqLU6q/kOaOdk29z6Q=
k8s.io/apimachinery v0.19.12 h1:XWmRhVo9vJOem6bLAyNnA31e5k1V06302wrIy35OA04=
k8s.io/apimachinery v0.19.12/go.mod h1:9eb44nUQSsz9QZiilFRuMj3ZbTmoWolU8S2gnXoRMjo=
k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA=
k8s.io/apiserver v0.19.7 h1:fOOELJ9TNC6DgKL3GUkQLE/EBMLjwBseTstx2eRP61o=
k8s.io/apiserver v0.19.7/go.mod h1:DmWVQggNePspa+vSsVytVbS3iBSDTXdJVt0akfHacKk=
k8s.io/apiserver v0.19.12 h1:xQjt/jLqdYszJTRTDDnHUnA0S+a1TZ/8rgWr0/xoa6I=
k8s.io/apiserver v0.19.12/go.mod h1:ldZAZTNIKfMMv/UUEhk6UyTXC0/34iRdNFHo+MJOPc4=
k8s.io/cli-runtime v0.19.2/go.mod h1:CMynmJM4Yf02TlkbhKxoSzi4Zf518PukJ5xep/NaNeY=
k8s.io/cli-runtime v0.19.7 h1:VkHsqrQYCD6+yBm2k9lOxLJtfo1tmb/TdYIHQ2RSCsY=
k8s.io/cli-runtime v0.19.7/go.mod h1:UTtbWaGV/USZSrnvuW/lRZGM5OsemAT/q/Du/Ac+wKU=
k8s.io/cli-runtime v0.19.12 h1:17snU0NcEnpU6TT5wcnBdBW/ydUQQ/qn82rKuAU5VkY=
k8s.io/cli-runtime v0.19.12/go.mod h1:KopjJ53HaHZjG+WhJmH8WxZzxnXVNkxP7GO1QiQJ2uI=
k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU=
k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
k8s.io/client-go v0.19.7 h1:SoJ4mzZ9LyXBGDe8MmpMznw0CwQ1ITWgsmG7GixvhUU=
k8s.io/client-go v0.19.7/go.mod h1:iytGI7S3kmv6bWnn+bSQUE4VlrEi4YFssvVB7J7Hvqg=
k8s.io/client-go v0.19.12 h1:bA6fG7J2cgY0pRKLQD6W15QSSZoPp9lHhg950BKQXjk=
k8s.io/client-go v0.19.12/go.mod h1:BAGKQraZ6fDmXhT46pGXWZQQqN7P4E0BJux0+9O6Gt0=
k8s.io/cluster-bootstrap v0.19.2 h1:6/LI5EnKCcB0QiDKIsTxoCOdKZtsSwr8Xm/tEhiMv78=
k8s.io/cluster-bootstrap v0.19.2/go.mod h1:bzngsppPfdt9vAHUnDIEoMNsxD2b6XArVVH/W9PDDFk=
k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0=
k8s.io/code-generator v0.19.12/go.mod h1:ADrDvaUQWGn4a8lX0ONtzb7uFmDRQOMSYIMk1qWIAx8=
k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo=
k8s.io/component-base v0.19.7 h1:ZXS2VRWOWBOc2fTd1zjzhi/b/mkqFT9FDqiNsn1cH30=
k8s.io/component-base v0.19.7/go.mod h1:YX8spPBgwl3I6UGcSdQiEMAqRMSUsGQOW7SEr4+Qa3U=
k8s.io/component-base v0.19.12 h1:pEsTceFc3Hs9qJX4h2XYXQDzRMCNzTO8MX34bhjq0Io=
k8s.io/component-base v0.19.12/go.mod h1:tpwExE0sY3A7CwtlxGL7SnQOdQfUlnFybT6GmAD+z/s=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
@@ -867,16 +859,22 @@ k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.3.0 h1:WmkrnW7fdrm0/DMClc+HIxtftvxVIPAhlVwMQo5yLco=
k8s.io/klog/v2 v2.3.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/kube-aggregator v0.19.12 h1:OwyNUe/7/gxzEnaLd3sC9Yrpx0fZAERzvFslX5Qq5g8=
k8s.io/kube-aggregator v0.19.12/go.mod h1:K76wPd03pSHEmS1FgJOcpryac5C3va4cbCvSu+4EmE0=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ=
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
k8s.io/kubectl v0.19.2/go.mod h1:4ib3oj5ma6gF95QukTvC7ZBMxp60+UEAhDPjLuBIrV4=
k8s.io/metrics v0.19.2/go.mod h1:IlLaAGXN0q7yrtB+SV0q3JIraf6VtlDr+iuTcX21fCU=
k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20200912215256-4140de9c8800 h1:9ZNvfPvVIEsp/T1ez4GQuzCcCTEQWhovSofhqR73A6g=
k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20201005171033-6301aaf42dc7 h1:XQ0OMFdRDkDIu0b1zqEKSZdWUD7I4bZ4d4nqr8CLKbQ=
k8s.io/utils v0.0.0-20201005171033-6301aaf42dc7/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/cluster-api v0.3.11-0.20210106212952-b6c1b5b3db3d h1:OwD6b5QiyY4JXWRu7bK0gxKv/x51akqGLPmD1kRJmmI=
sigs.k8s.io/cluster-api v0.3.11-0.20210106212952-b6c1b5b3db3d/go.mod h1:HwDMTKYusSK0SnTAr/P3htrxdiykBADqffkamkiUXhk=
sigs.k8s.io/controller-runtime v0.7.1-0.20201215171748-096b2e07c091 h1:tqrTDj7mJmM6TdpoM1rN2PzBRH9yzCReqKGMy4sp+f0=
@@ -885,8 +883,9 @@ sigs.k8s.io/kind v0.9.0/go.mod h1:cxKQWwmbtRDzQ+RNKnR6gZG6fjbeTtItp5cGf+ww+1Y=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA=
sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.3 h1:4oyYo8NREp49LBBhKxEqCulFjg26rawYKrnCmg+Sr6c=
sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -41,6 +41,11 @@ if [[ -z "${VERSION}" ]]; then
exit 1
fi
if [[ -z "${REGISTRY}" ]]; then
echo "REGISTRY must be set"
exit 1
fi
if [[ -z "${GIT_SHA}" ]]; then
echo "GIT_SHA must be set"
exit 1
@@ -59,6 +64,7 @@ fi
export CGO_ENABLED=0
LDFLAGS="-X ${PKG}/pkg/buildinfo.Version=${VERSION}"
LDFLAGS="${LDFLAGS} -X ${PKG}/pkg/buildinfo.ImageRegistry=${REGISTRY}"
LDFLAGS="${LDFLAGS} -X ${PKG}/pkg/buildinfo.GitSHA=${GIT_SHA}"
LDFLAGS="${LDFLAGS} -X ${PKG}/pkg/buildinfo.GitTreeState=${GIT_TREE_STATE}"

136
hack/crd-gen/v1/main.go Normal file
View File

@@ -0,0 +1,136 @@
/*
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.
*/
// This code embeds the CRD manifests in config/crd/v1/bases in
// config/crd/v1/crds/crds.go.
package main
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"text/template"
)
// This is relative to config/crd/crds
const goHeaderFile = "../../../../hack/boilerplate.go.txt"
const tpl = `{{.GoHeader}}
// Code generated by crds_generate.go; DO NOT EDIT.
package crds
import (
"bytes"
"compress/gzip"
"io/ioutil"
apiextinstall "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/client-go/kubernetes/scheme"
)
var rawCRDs = [][]byte{
{{- range .RawCRDs }}
[]byte({{ . }}),
{{- end }}
}
var CRDs = crds()
func crds() []*apiextv1.CustomResourceDefinition {
apiextinstall.Install(scheme.Scheme)
decode := scheme.Codecs.UniversalDeserializer().Decode
var objs []*apiextv1.CustomResourceDefinition
for _, crd := range rawCRDs {
gzr, err := gzip.NewReader(bytes.NewReader(crd))
if err != nil {
panic(err)
}
bytes, err := ioutil.ReadAll(gzr)
if err != nil {
panic(err)
}
gzr.Close()
obj, _, err := decode(bytes, nil, nil)
if err != nil {
panic(err)
}
objs = append(objs, obj.(*apiextv1.CustomResourceDefinition))
}
return objs
}
`
type templateData struct {
GoHeader string
RawCRDs []string
}
func main() {
headerBytes, err := ioutil.ReadFile(goHeaderFile)
if err != nil {
log.Fatalln(err)
}
data := templateData{
GoHeader: string(headerBytes),
}
// This is relative to config/crd/crds
manifests, err := ioutil.ReadDir("../bases")
if err != nil {
log.Fatalln(err)
}
for _, crd := range manifests {
file, err := os.Open("../bases/" + crd.Name())
if err != nil {
log.Fatalln(err)
}
// gzip compress manifest
var buf bytes.Buffer
gzw := gzip.NewWriter(&buf)
if _, err := io.Copy(gzw, file); err != nil {
log.Fatalln(err)
}
file.Close()
gzw.Close()
data.RawCRDs = append(data.RawCRDs, fmt.Sprintf("%q", buf.Bytes()))
}
t, err := template.New("crd").Parse(tpl)
if err != nil {
log.Fatalln(err)
}
out, err := os.Create("crds.go")
if err != nil {
log.Fatalln(err)
}
if err := t.Execute(out, data); err != nil {
log.Fatalln(err)
}
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2019 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// This code embeds the CRD manifests in config/crd/bases in
// config/crd/crds/crds.go.
// This code embeds the CRD manifests in config/crd/v1beta1/bases in
// config/crd/v1beta1/crds/crds.go.
package main
@@ -31,7 +31,7 @@ import (
)
// This is relative to config/crd/crds
const goHeaderFile = "../../../hack/boilerplate.go.txt"
const goHeaderFile = "../../../../hack/boilerplate.go.txt"
const tpl = `{{.GoHeader}}
// Code generated by crds_generate.go; DO NOT EDIT.

View File

@@ -91,6 +91,9 @@ echo "BUILDX_PLATFORMS: $BUILDX_PLATFORMS"
echo "Building and pushing container images."
# The use of "registry" as the buildx output type below instructs
# Docker to push the image
VERSION="$VERSION" \
TAG_LATEST="$TAG_LATEST" \
BUILDX_PLATFORMS="$BUILDX_PLATFORMS" \

View File

@@ -29,6 +29,11 @@ if [ -z "${RELEASE_NOTES_FILE}" ]; then
exit 1
fi
if [ -z "${REGISTRY}" ]; then
echo "REGISTRY must be set"
exit 1
fi
GIT_DIRTY=$(git status --porcelain 2> /dev/null)
if [[ -z "${GIT_DIRTY}" ]]; then
export GIT_TREE_STATE=clean

View File

@@ -0,0 +1,3 @@
[
{ "op": "replace", "path": "/spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/hooks/properties/resources/items/properties/postHooks/items/properties/init/properties/initContainers/items/properties/ports/items/required", "value": [ "containerPort", "protocol"] }
]

View File

@@ -1,6 +1,6 @@
#!/bin/bash
#
# Copyright 2017 the Velero contributors.
# Copyright the Velero contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -44,20 +44,24 @@ ${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \
--output-base ../../.. \
$@
# Generate manifests e.g. CRD, RBAC etc.
controller-gen \
crd:crdVersions=v1beta1,preserveUnknownFields=false,trivialVersions=true \
paths=./pkg/apis/velero/v1/... \
paths=./pkg/controller/... \
output:crd:artifacts:config=config/crd/bases
# Generate both apiextensions.k8s.io/v1beta1 and apiextensions.k8s.io/v1
for version in v1beta1 v1
do
# Generate manifests e.g. CRD, RBAC etc.
controller-gen \
crd:crdVersions=$version,preserveUnknownFields=false,trivialVersions=true \
paths=./pkg/apis/velero/v1/... \
paths=./pkg/controller/... \
output:crd:artifacts:config=config/crd/$version/bases
# this is a super hacky workaround for https://github.com/kubernetes/kubernetes/issues/91395
# which a result of fixing the validation on CRD objects. The validation ensures the fields that are list map keys, are either marked
# as required or have default values to ensure merging of list map items work as expected.
# With "containerPort" and "protocol" being considered as x-kubernetes-list-map-keys in the container ports, and "protocol" was not
# a required field, the CRD would fail validation with errors similar to the one reported in https://github.com/kubernetes/kubernetes/issues/91395.
# once controller-gen (above) is able to generate CRDs with `protocol` as a required field, this hack can be removed.
kubectl patch -f config/crd/bases/velero.io_restores.yaml -p "$(cat hack/restore-crd-patch.json)" --type=json --local=true -o yaml > /tmp/velero.io_restores-yaml.patched
mv /tmp/velero.io_restores-yaml.patched config/crd/bases/velero.io_restores.yaml
# this is a super hacky workaround for https://github.com/kubernetes/kubernetes/issues/91395
# which a result of fixing the validation on CRD objects. The validation ensures the fields that are list map keys, are either marked
# as required or have default values to ensure merging of list map items work as expected.
# With "containerPort" and "protocol" being considered as x-kubernetes-list-map-keys in the container ports, and "protocol" was not
# a required field, the CRD would fail validation with errors similar to the one reported in https://github.com/kubernetes/kubernetes/issues/91395.
# once controller-gen (above) is able to generate CRDs with `protocol` as a required field, this hack can be removed.
kubectl patch -f config/crd/$version/bases/velero.io_restores.yaml -p "$(cat hack/restore-crd-patch-$version.json)" --type=json --local=true -o yaml > /tmp/velero.io_restores-yaml.patched
mv /tmp/velero.io_restores-yaml.patched config/crd/$version/bases/velero.io_restores.yaml
go generate ./config/crd/crds
go generate ./config/crd/$version/crds
done

View File

@@ -1,6 +1,6 @@
#!/bin/bash -e
#
# Copyright 2017 the Velero contributors.
# Copyright the Velero contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -19,11 +19,14 @@ HACK_DIR=$(dirname "${BASH_SOURCE}")
${HACK_DIR}/update-generated-crd-code.sh --verify-only
# ensure no changes to generated CRDs
if ! git diff --exit-code config/crd/crds/crds.go >/dev/null; then
# revert changes to state before running CRD generation to stay consistent
# with code-generator `--verify-only` option which discards generated changes
git checkout config/crd
for version in v1beta1 v1
do
if ! git diff --exit-code config/crd/$version/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
echo "CRD verification - failed! Generated CRDs are out-of-date, please run 'make update' and 'git add' the generated file(s)."
exit 1
fi
echo "CRD verification - failed! Generated CRDs are out-of-date, please run 'make update' and 'git add' the generated file(s)."
exit 1
fi
done

51
internal/velero/images.go Normal file
View File

@@ -0,0 +1,51 @@
/*
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 velero
import (
"fmt"
"github.com/vmware-tanzu/velero/pkg/buildinfo"
)
// Use Dockerhub as the default registry if the build process didn't supply a registry
func imageRegistry() string {
if buildinfo.ImageRegistry == "" {
return "velero"
}
return buildinfo.ImageRegistry
}
// ImageTag returns the image tag that should be used by Velero images.
// It uses the Version from the buildinfo or "latest" if the build process didn't supply a version.
func ImageTag() string {
if buildinfo.Version == "" {
return "latest"
}
return buildinfo.Version
}
// DefaultVeleroImage returns the default container image to use for this version of Velero.
func DefaultVeleroImage() string {
return fmt.Sprintf("%s/%s:%s", imageRegistry(), "velero", ImageTag())
}
// DefaultResticRestoreHelperImage returns the default container image to use for the restic restore helper
// for this version of Velero.
func DefaultResticRestoreHelperImage() string {
return fmt.Sprintf("%s/%s:%s", imageRegistry(), "velero-restic-restore-helper", ImageTag())
}

View File

@@ -0,0 +1,140 @@
/*
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 velero
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/vmware-tanzu/velero/pkg/buildinfo"
)
func TestImageTag(t *testing.T) {
testCases := []struct {
name string
buildInfoVersion string
want string
}{
{
name: "tag is latest when buildinfo.Version is empty",
want: "latest",
},
{
name: "tag is buildinfo.Version when not empty",
buildInfoVersion: "custom-build-version",
want: "custom-build-version",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
originalVersion := buildinfo.Version
buildinfo.Version = tc.buildInfoVersion
defer func() {
buildinfo.Version = originalVersion
}()
assert.Equal(t, tc.want, ImageTag())
})
}
}
func TestImageRegistry(t *testing.T) {
testCases := []struct {
name string
buildInfoRegistry string
want string
}{
{
name: "registry is velero when buildinfo.ImageRegistry is empty",
want: "velero",
},
{
name: "registry is buildinfo.ImageRegistry when not empty",
buildInfoRegistry: "custom-build-image-registry",
want: "custom-build-image-registry",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
originalImageRegistry := buildinfo.ImageRegistry
buildinfo.ImageRegistry = tc.buildInfoRegistry
defer func() {
buildinfo.ImageRegistry = originalImageRegistry
}()
assert.Equal(t, tc.want, imageRegistry())
})
}
}
func testDefaultImage(t *testing.T, defaultImageFn func() string, imageName string) {
testCases := []struct {
name string
buildInfoVersion string
buildInfoRegistry string
want string
}{
{
name: "image uses velero as registry and latest as tag when buildinfo.ImageRegistry and buildinfo.Version are empty",
want: fmt.Sprintf("velero/%s:latest", imageName),
},
{
name: "image uses buildinfo.ImageRegistry as registry when not empty",
buildInfoRegistry: "custom-build-image-registry",
want: fmt.Sprintf("custom-build-image-registry/%s:latest", imageName),
},
{
name: "image uses buildinfo.Version as tag when not empty",
buildInfoVersion: "custom-build-version",
want: fmt.Sprintf("velero/%s:custom-build-version", imageName),
},
{
name: "image uses both buildinfo.ImageRegistry and buildinfo.Version when not empty",
buildInfoRegistry: "custom-build-image-registry",
buildInfoVersion: "custom-build-version",
want: fmt.Sprintf("custom-build-image-registry/%s:custom-build-version", imageName),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
originalImageRegistry := buildinfo.ImageRegistry
originalVersion := buildinfo.Version
buildinfo.ImageRegistry = tc.buildInfoRegistry
buildinfo.Version = tc.buildInfoVersion
defer func() {
buildinfo.ImageRegistry = originalImageRegistry
buildinfo.Version = originalVersion
}()
assert.Equal(t, tc.want, defaultImageFn())
})
}
}
func TestDefaultVeleroImage(t *testing.T) {
testDefaultImage(t, DefaultVeleroImage, "velero")
}
func TestDefaultResticRestoreHelperImage(t *testing.T) {
testDefaultImage(t, DefaultResticRestoreHelperImage, "velero-restic-restore-helper")
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2020 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -767,9 +767,9 @@ func TestCRDInclusion(t *testing.T) {
Result(),
apiResources: []*test.APIResource{
test.CRDs(
builder.ForCustomResourceDefinition("backups.velero.io").Result(),
builder.ForCustomResourceDefinition("volumesnapshotlocations.velero.io").Result(),
builder.ForCustomResourceDefinition("test.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("backups.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("volumesnapshotlocations.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("test.velero.io").Result(),
),
test.VSLs(
builder.ForVolumeSnapshotLocation("foo", "vsl-1").Result(),
@@ -793,9 +793,9 @@ func TestCRDInclusion(t *testing.T) {
Result(),
apiResources: []*test.APIResource{
test.CRDs(
builder.ForCustomResourceDefinition("backups.velero.io").Result(),
builder.ForCustomResourceDefinition("volumesnapshotlocations.velero.io").Result(),
builder.ForCustomResourceDefinition("test.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("backups.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("volumesnapshotlocations.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("test.velero.io").Result(),
),
test.VSLs(
builder.ForVolumeSnapshotLocation("foo", "vsl-1").Result(),
@@ -813,9 +813,9 @@ func TestCRDInclusion(t *testing.T) {
Result(),
apiResources: []*test.APIResource{
test.CRDs(
builder.ForCustomResourceDefinition("backups.velero.io").Result(),
builder.ForCustomResourceDefinition("volumesnapshotlocations.velero.io").Result(),
builder.ForCustomResourceDefinition("test.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("backups.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("volumesnapshotlocations.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("test.velero.io").Result(),
),
test.VSLs(
builder.ForVolumeSnapshotLocation("foo", "vsl-1").Result(),
@@ -839,9 +839,9 @@ func TestCRDInclusion(t *testing.T) {
Result(),
apiResources: []*test.APIResource{
test.CRDs(
builder.ForCustomResourceDefinition("backups.velero.io").Result(),
builder.ForCustomResourceDefinition("volumesnapshotlocations.velero.io").Result(),
builder.ForCustomResourceDefinition("test.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("backups.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("volumesnapshotlocations.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("test.velero.io").Result(),
),
test.VSLs(
builder.ForVolumeSnapshotLocation("foo", "vsl-1").Result(),
@@ -862,9 +862,9 @@ func TestCRDInclusion(t *testing.T) {
Result(),
apiResources: []*test.APIResource{
test.CRDs(
builder.ForCustomResourceDefinition("backups.velero.io").Result(),
builder.ForCustomResourceDefinition("volumesnapshotlocations.velero.io").Result(),
builder.ForCustomResourceDefinition("test.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("backups.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("volumesnapshotlocations.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("test.velero.io").Result(),
),
test.VSLs(
builder.ForVolumeSnapshotLocation("foo", "vsl-1").Result(),
@@ -883,9 +883,9 @@ func TestCRDInclusion(t *testing.T) {
Result(),
apiResources: []*test.APIResource{
test.CRDs(
builder.ForCustomResourceDefinition("backups.velero.io").Result(),
builder.ForCustomResourceDefinition("volumesnapshotlocations.velero.io").Result(),
builder.ForCustomResourceDefinition("test.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("backups.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("volumesnapshotlocations.velero.io").Result(),
builder.ForCustomResourceDefinitionV1Beta1("test.velero.io").Result(),
),
test.VSLs(
builder.ForVolumeSnapshotLocation("foo", "vsl-1").Result(),

View File

@@ -1,5 +1,5 @@
/*
Copyright 2020 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ func TestRemapCRDVersionAction(t *testing.T) {
// build a v1beta1 CRD with the same name and add it to the fake client that the plugin is going to call.
// keep the same one for all 3 tests, since there's little value in recreating it
b := builder.ForCustomResourceDefinition("test.velero.io")
b := builder.ForCustomResourceDefinitionV1Beta1("test.velero.io")
c := b.Result()
_, err := betaClient.Create(context.TODO(), c, metav1.CreateOptions{})
require.NoError(t, err)

View File

@@ -45,7 +45,7 @@ func ForPluginContainer(image string, pullPolicy corev1api.PullPolicy) *Containe
// getName returns the 'name' component of a docker
// image that includes the entire string except the registry name, and transforms the combined
// string into a DNS-1123 compatible name.
// string into a RFC-1123 compatible name.
func getName(image string) string {
slashIndex := strings.Index(image, "/")
slashCount := 0
@@ -67,7 +67,14 @@ func getName(image string) string {
end = colonIndex
}
return strings.Replace(image[start:end], "/", "-", -1) // this makes it DNS-1123 compatible
// https://github.com/distribution/distribution/blob/main/docs/spec/api.md#overview
// valid repository names match the regex [a-z0-9]+(?:[._-][a-z0-9]+)*
// image repository names can container [._] but [._] are not allowed in RFC-1123 labels.
// replace '/', '_' and '.' with '-'
re := strings.NewReplacer("/", "-",
"_", "-",
".", "-")
return re.Replace(image[start:end])
}
// Result returns the built Container.

View File

@@ -75,7 +75,17 @@ func TestGetName(t *testing.T) {
{
name: "image name with registry hostname starting with a / will include the registry name ¯\\_(ツ)_/¯",
image: "/gcr.io/my-repo/mystery/another/my-image",
expected: "gcr.io-my-repo-mystery-another-my-image",
expected: "gcr-io-my-repo-mystery-another-my-image",
},
{
name: "image repository names containing _ ",
image: "projects.registry.vmware.com/tanzu_migrator/route-2-httpproxy:myTag",
expected: "tanzu-migrator-route-2-httpproxy",
},
{
name: "image repository names containing . ",
image: "projects.registry.vmware.com/tanzu.migrator/route-2-httpproxy:myTag",
expected: "tanzu-migrator-route-2-httpproxy",
},
}

View File

@@ -1,91 +0,0 @@
/*
Copyright 2019 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// CustomResourceDefinitionBuilder builds CustomResourceDefinition objects.
type CustomResourceDefinitionBuilder struct {
object *apiextv1beta1.CustomResourceDefinition
}
// ForCustomResourceDefinition is the constructor for a CustomResourceDefinitionBuilder.
func ForCustomResourceDefinition(name string) *CustomResourceDefinitionBuilder {
return &CustomResourceDefinitionBuilder{
object: &apiextv1beta1.CustomResourceDefinition{
TypeMeta: metav1.TypeMeta{
APIVersion: apiextv1beta1.SchemeGroupVersion.String(),
Kind: "CustomResourceDefinition",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
},
}
}
// Condition adds a CustomResourceDefinitionCondition objects to a CustomResourceDefinitionBuilder.
func (c *CustomResourceDefinitionBuilder) Condition(cond apiextv1beta1.CustomResourceDefinitionCondition) *CustomResourceDefinitionBuilder {
c.object.Status.Conditions = append(c.object.Status.Conditions, cond)
return c
}
// Result returns the built CustomResourceDefinition.
func (b *CustomResourceDefinitionBuilder) Result() *apiextv1beta1.CustomResourceDefinition {
return b.object
}
// ObjectMeta applies functional options to the CustomResourceDefinition's ObjectMeta.
func (b *CustomResourceDefinitionBuilder) ObjectMeta(opts ...ObjectMetaOpt) *CustomResourceDefinitionBuilder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// CustomResourceDefinitionConditionBuilder builds CustomResourceDefinitionCondition objects.
type CustomResourceDefinitionConditionBuilder struct {
object apiextv1beta1.CustomResourceDefinitionCondition
}
// ForCustomResourceDefinitionConditionBuilder is the construction for a CustomResourceDefinitionConditionBuilder.
func ForCustomResourceDefinitionCondition() *CustomResourceDefinitionConditionBuilder {
return &CustomResourceDefinitionConditionBuilder{
object: apiextv1beta1.CustomResourceDefinitionCondition{},
}
}
// Type sets the Condition's type.
func (c *CustomResourceDefinitionConditionBuilder) Type(t apiextv1beta1.CustomResourceDefinitionConditionType) *CustomResourceDefinitionConditionBuilder {
c.object.Type = t
return c
}
// Status sets the Condition's status.
func (c *CustomResourceDefinitionConditionBuilder) Status(cs apiextv1beta1.ConditionStatus) *CustomResourceDefinitionConditionBuilder {
c.object.Status = cs
return c
}
// Results returns the built CustomResourceDefinitionCondition.
func (b *CustomResourceDefinitionConditionBuilder) Result() apiextv1beta1.CustomResourceDefinitionCondition {
return b.object
}

View File

@@ -0,0 +1,91 @@
/*
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package builder
import (
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// CustomResourceDefinitionV1Beta1Builder builds v1beta1 CustomResourceDefinition objects.
type CustomResourceDefinitionV1Beta1Builder struct {
object *apiextv1beta1.CustomResourceDefinition
}
// ForCustomResourceDefinitionV1Beta1 is the constructor for a CustomResourceDefinitionV1Beta1Builder.
func ForCustomResourceDefinitionV1Beta1(name string) *CustomResourceDefinitionV1Beta1Builder {
return &CustomResourceDefinitionV1Beta1Builder{
object: &apiextv1beta1.CustomResourceDefinition{
TypeMeta: metav1.TypeMeta{
APIVersion: apiextv1beta1.SchemeGroupVersion.String(),
Kind: "CustomResourceDefinition",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
},
}
}
// Condition adds a CustomResourceDefinitionCondition objects to a CustomResourceDefinitionV1Beta1Builder.
func (c *CustomResourceDefinitionV1Beta1Builder) Condition(cond apiextv1beta1.CustomResourceDefinitionCondition) *CustomResourceDefinitionV1Beta1Builder {
c.object.Status.Conditions = append(c.object.Status.Conditions, cond)
return c
}
// Result returns the built CustomResourceDefinition.
func (b *CustomResourceDefinitionV1Beta1Builder) Result() *apiextv1beta1.CustomResourceDefinition {
return b.object
}
// ObjectMeta applies functional options to the CustomResourceDefinition's ObjectMeta.
func (b *CustomResourceDefinitionV1Beta1Builder) ObjectMeta(opts ...ObjectMetaOpt) *CustomResourceDefinitionV1Beta1Builder {
for _, opt := range opts {
opt(b.object)
}
return b
}
// CustomResourceDefinitionV1Beta1ConditionBuilder builds CustomResourceDefinitionV1Beta1Condition objects.
type CustomResourceDefinitionV1Beta1ConditionBuilder struct {
object apiextv1beta1.CustomResourceDefinitionCondition
}
// ForCustomResourceDefinitionV1Beta1Condition is the construction for a CustomResourceDefinitionV1Beta1ConditionBuilder.
func ForCustomResourceDefinitionV1Beta1Condition() *CustomResourceDefinitionV1Beta1ConditionBuilder {
return &CustomResourceDefinitionV1Beta1ConditionBuilder{
object: apiextv1beta1.CustomResourceDefinitionCondition{},
}
}
// Type sets the Condition's type.
func (c *CustomResourceDefinitionV1Beta1ConditionBuilder) Type(t apiextv1beta1.CustomResourceDefinitionConditionType) *CustomResourceDefinitionV1Beta1ConditionBuilder {
c.object.Type = t
return c
}
// Status sets the Condition's status.
func (c *CustomResourceDefinitionV1Beta1ConditionBuilder) Status(cs apiextv1beta1.ConditionStatus) *CustomResourceDefinitionV1Beta1ConditionBuilder {
c.object.Status = cs
return c
}
// Result returns the built v1beta1 CustomResourceDefinitionCondition.
func (c *CustomResourceDefinitionV1Beta1ConditionBuilder) Result() apiextv1beta1.CustomResourceDefinitionCondition {
return c.object
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2017 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -31,6 +31,10 @@ var (
// GitTreeState indicates if the git tree is clean or dirty, set by the go linker's -X flag at build
// time.
GitTreeState string
// ImageRegistry is the image registry that this build of Velero should use by default to pull the
// Velero and Restic Restore Helper images from.
ImageRegistry string
)
// FormattedGitSHA renders the Git SHA with an indicator of the tree state.

View File

@@ -19,6 +19,9 @@ package client
import (
"os"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
k8scheme "k8s.io/client-go/kubernetes/scheme"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/pkg/errors"
@@ -45,7 +48,8 @@ type Factory interface {
// DynamicClient returns a Kubernetes dynamic client. It uses the following priority to specify the cluster
// configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration.
DynamicClient() (dynamic.Interface, error)
// KubebuilderClient returns a Kubernetes dynamic client. It uses the following priority to specify the cluster
// KubebuilderClient returns a client for the controller runtime framework. It adds Kubernetes and Velero
// types to its scheme. It uses the following priority to specify the cluster
// configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration.
KubebuilderClient() (kbclient.Client, error)
// SetBasename changes the basename for an already-constructed client.
@@ -151,6 +155,9 @@ func (f *factory) KubebuilderClient() (kbclient.Client, error) {
scheme := runtime.NewScheme()
velerov1api.AddToScheme(scheme)
k8scheme.AddToScheme(scheme)
apiextv1beta1.AddToScheme(scheme)
apiextv1.AddToScheme(scheme)
kubebuilderClient, err := kbclient.New(clientConfig, kbclient.Options{
Scheme: scheme,
})

View File

@@ -1,5 +1,5 @@
/*
Copyright 2020 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -25,15 +25,19 @@ import (
"time"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/vmware-tanzu/velero/internal/velero"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/client"
"github.com/vmware-tanzu/velero/pkg/cmd"
"github.com/vmware-tanzu/velero/pkg/cmd/util/flag"
"github.com/vmware-tanzu/velero/pkg/cmd/util/output"
velerodiscovery "github.com/vmware-tanzu/velero/pkg/discovery"
"github.com/vmware-tanzu/velero/pkg/install"
kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube"
)
@@ -68,6 +72,7 @@ type InstallOptions struct {
Plugins flag.StringArray
NoDefaultBackupLocation bool
CRDsOnly bool
CRDsVersion string
CACertFile string
Features string
DefaultVolumesToRestic bool
@@ -102,6 +107,7 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) {
flags.DurationVar(&o.DefaultResticMaintenanceFrequency, "default-restic-prune-frequency", o.DefaultResticMaintenanceFrequency, "How often 'restic prune' is run for restic repositories by default. Optional.")
flags.Var(&o.Plugins, "plugins", "Plugin container images to install into the Velero Deployment")
flags.BoolVar(&o.CRDsOnly, "crds-only", o.CRDsOnly, "Only generate CustomResourceDefinition resources. Useful for updating CRDs for an existing Velero install.")
flags.StringVar(&o.CRDsVersion, "crds-version", o.CRDsVersion, "The version to generate CustomResourceDefinition resources if Velero can't discover the Kubernetes preferred CRD API version. Optional.")
flags.StringVar(&o.CACertFile, "cacert", o.CACertFile, "File containing a certificate bundle to use when verifying TLS connections to the object store. Optional.")
flags.StringVar(&o.Features, "features", o.Features, "Comma separated list of Velero feature flags to be set on the Velero deployment and the restic daemonset, if restic is enabled")
flags.BoolVar(&o.DefaultVolumesToRestic, "default-volumes-to-restic", o.DefaultVolumesToRestic, "Bool flag to configure Velero server to use restic by default to backup all pod volumes on all backups. Optional.")
@@ -111,7 +117,7 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) {
func NewInstallOptions() *InstallOptions {
return &InstallOptions{
Namespace: velerov1api.DefaultNamespace,
Image: install.DefaultImage,
Image: velero.DefaultVeleroImage(),
BackupStorageConfig: flag.NewMap(),
VolumeSnapshotConfig: flag.NewMap(),
PodAnnotations: flag.NewMap(),
@@ -128,6 +134,7 @@ func NewInstallOptions() *InstallOptions {
UseVolumeSnapshots: true,
NoDefaultBackupLocation: false,
CRDsOnly: false,
CRDsVersion: "v1",
DefaultVolumesToRestic: false,
}
}
@@ -186,6 +193,7 @@ func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) {
NoDefaultBackupLocation: o.NoDefaultBackupLocation,
CACertData: caCertData,
Features: strings.Split(o.Features, ","),
CRDsVersion: o.CRDsVersion,
DefaultVolumesToRestic: o.DefaultVolumesToRestic,
}, nil
}
@@ -246,19 +254,37 @@ This is useful as a starting point for more customized installations.
// Run executes a command in the context of the provided arguments.
func (o *InstallOptions) Run(c *cobra.Command, f client.Factory) error {
// Find the kube-apiserver group apiextensions.k8s.io preferred API version
clientset, err := f.KubeClient()
if err == nil {
// kubeconfig available
discoveryHelper, err := velerodiscovery.NewHelper(clientset.Discovery(), &logrus.Logger{})
if err == nil {
// kubernetes apiserver available
gvr, _, err := discoveryHelper.ResourceFor(
schema.GroupVersionResource{
Group: "apiextensions.k8s.io",
Resource: "customresourcedefinitions",
})
if err != nil {
return err
}
// Update the group apiextensions.k8s.io preferred API version
o.CRDsVersion = gvr.Version
}
}
var resources *unstructured.UnstructuredList
if o.CRDsOnly {
resources = install.AllCRDs()
resources = install.AllCRDs(o.CRDsVersion)
} else {
vo, err := o.AsVeleroOptions()
if err != nil {
return err
}
resources, err = install.AllResources(vo)
if err != nil {
return err
}
resources = install.AllResources(vo)
}
if _, err := output.PrintWithFormat(c, resources); err != nil {
@@ -272,24 +298,28 @@ func (o *InstallOptions) Run(c *cobra.Command, f client.Factory) error {
if err != nil {
return err
}
factory := client.NewDynamicFactory(dynamicClient)
dynamicFactory := client.NewDynamicFactory(dynamicClient)
kbClient, err := f.KubebuilderClient()
if err != nil {
return err
}
errorMsg := fmt.Sprintf("\n\nError installing Velero. Use `kubectl logs deploy/velero -n %s` to check the deploy logs", o.Namespace)
err = install.Install(factory, resources, os.Stdout)
err = install.Install(dynamicFactory, kbClient, resources, os.Stdout)
if err != nil {
return errors.Wrap(err, errorMsg)
}
if o.Wait {
fmt.Println("Waiting for Velero deployment to be ready.")
if _, err = install.DeploymentIsReady(factory, o.Namespace); err != nil {
if _, err = install.DeploymentIsReady(dynamicFactory, o.Namespace); err != nil {
return errors.Wrap(err, errorMsg)
}
if o.UseRestic {
fmt.Println("Waiting for Velero restic daemonset to be ready.")
if _, err = install.DaemonSetIsReady(factory, o.Namespace); err != nil {
if _, err = install.DaemonSetIsReady(dynamicFactory, o.Namespace); err != nil {
return errors.Wrap(err, errorMsg)
}
}
@@ -318,6 +348,11 @@ func (o *InstallOptions) Validate(c *cobra.Command, args []string, f client.Fact
return err
}
// Check the CRD version is valid.
if o.CRDsVersion != "v1beta1" && o.CRDsVersion != "v1" {
return errors.Errorf("CRD version must be v1beta1 or v1")
}
// If we're only installing CRDs, we can skip the rest of the validation.
if o.CRDsOnly {
return nil

View File

@@ -26,20 +26,19 @@ import (
"github.com/spf13/pflag"
corev1 "k8s.io/api/core/v1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/labels"
kubeerrs "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/vmware-tanzu/velero/pkg/client"
"github.com/vmware-tanzu/velero/pkg/cmd"
"github.com/vmware-tanzu/velero/pkg/cmd/cli"
"github.com/vmware-tanzu/velero/pkg/install"
"github.com/vmware-tanzu/velero/pkg/util/kube"
)
// uninstallOptions collects all the options for uninstalling Velero from a Kubernetes cluster.
@@ -78,9 +77,9 @@ Use '--force' to skip the prompt confirming if you want to uninstall Velero.
}
}
client, extCl, err := kube.GetClusterClient()
kbClient, err := f.KubebuilderClient()
cmd.CheckError(err)
cmd.CheckError(Run(context.Background(), client, extCl, f.Namespace(), o.wait))
cmd.CheckError(Run(context.Background(), kbClient, f.Namespace(), o.wait))
},
}
@@ -89,54 +88,74 @@ Use '--force' to skip the prompt confirming if you want to uninstall Velero.
}
// Run removes all components that were deployed using the Velero install command
func Run(ctx context.Context, client *kubernetes.Clientset, extensionsClient *apiextensionsclientset.Clientset, namespace string, waitToTerminate bool) error {
func Run(ctx context.Context, kbClient kbclient.Client, namespace string, waitToTerminate bool) error {
var errs []error
// namespace
ns, err := client.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{})
if err != nil {
ns := &corev1.Namespace{}
key := kbclient.ObjectKey{Name: namespace}
if err := kbClient.Get(ctx, key, ns); err != nil {
if apierrors.IsNotFound(err) {
fmt.Printf("Velero installation namespace %q does not exist, skipping.\n", namespace)
fmt.Printf("Velero namespace %q does not exist, skipping.\n", namespace)
} else {
errs = append(errs, errors.WithStack(err))
}
} else {
if ns.Status.Phase == corev1.NamespaceTerminating {
fmt.Printf("Velero installation namespace %q is terminating.\n", namespace)
fmt.Printf("Velero namespace %q is terminating.\n", namespace)
} else {
err = client.CoreV1().Namespaces().Delete(ctx, ns.Name, metav1.DeleteOptions{})
if err != nil {
if err := kbClient.Delete(ctx, ns); err != nil {
errs = append(errs, errors.WithStack(err))
}
}
}
// rolebinding
// ClusterRoleBinding
crb := install.ClusterRoleBinding(namespace)
if err := client.RbacV1().ClusterRoleBindings().Delete(ctx, crb.Name, metav1.DeleteOptions{}); err != nil {
key = kbclient.ObjectKey{Name: crb.Name}
if err := kbClient.Get(ctx, key, crb); err != nil {
if apierrors.IsNotFound(err) {
fmt.Printf("Velero installation clusterrolebinding %q does not exist, skipping.\n", crb.Name)
fmt.Printf("Velero ClusterRoleBinding %q does not exist, skipping.\n", crb.Name)
} else {
errs = append(errs, errors.WithStack(err))
}
} else {
if err := kbClient.Delete(ctx, crb); err != nil {
errs = append(errs, errors.WithStack(err))
}
}
// CRDs
veleroLabels := labels.FormatLabels(install.Labels())
crds, err := extensionsClient.ApiextensionsV1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{
LabelSelector: veleroLabels,
})
if err != nil {
errs = append(errs, errors.WithStack(err))
veleroLabelSelector := labels.SelectorFromSet(install.Labels())
opts := []kbclient.DeleteAllOfOption{
kbclient.InNamespace(namespace),
kbclient.MatchingLabelsSelector{
Selector: veleroLabelSelector,
},
}
if len(crds.Items) == 0 {
fmt.Print("Velero CRDs do not exist, skipping.\n")
v1CRDsRemoved := false
v1crd := &apiextv1.CustomResourceDefinition{}
if err := kbClient.DeleteAllOf(ctx, v1crd, opts...); err != nil {
if meta.IsNoMatchError(err) {
fmt.Println("V1 Velero CRDs not found, skipping...")
} else {
errs = append(errs, errors.WithStack(err))
}
} else {
for _, removeCRD := range crds.Items {
if err = extensionsClient.ApiextensionsV1().CustomResourceDefinitions().Delete(ctx, removeCRD.ObjectMeta.Name, metav1.DeleteOptions{}); err != nil {
err2 := errors.WithMessagef(err, "Uninstall failed removing CRD %s", removeCRD.ObjectMeta.Name)
errs = append(errs, errors.WithStack(err2))
v1CRDsRemoved = true
}
// Remove any old Velero v1beta1 CRDs hanging around.
v1beta1crd := &apiextv1beta1.CustomResourceDefinition{}
if err := kbClient.DeleteAllOf(ctx, v1beta1crd, opts...); err != nil {
if meta.IsNoMatchError(err) {
if !v1CRDsRemoved {
// Only mention this if there were no V1 CRDs removed
fmt.Println("V1Beta1 Velero CRDs not found, skipping...")
}
} else {
errs = append(errs, errors.WithStack(err))
}
}
@@ -147,7 +166,7 @@ func Run(ctx context.Context, client *kubernetes.Clientset, extensionsClient *ap
defer cancel()
checkFunc := func() {
_, err := client.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{})
err := kbClient.Get(ctx, key, ns)
if err != nil {
if apierrors.IsNotFound(err) {
fmt.Print("\n")

View File

@@ -54,6 +54,7 @@ func NewCommand(f client.Factory) *cobra.Command {
RegisterRestoreItemAction("velero.io/cluster-role-bindings", newClusterRoleBindingItemAction).
RegisterRestoreItemAction("velero.io/crd-preserve-fields", newCRDV1PreserveUnknownFieldsItemAction).
RegisterRestoreItemAction("velero.io/change-pvc-node-selector", newChangePVCNodeSelectorItemAction(f)).
RegisterRestoreItemAction("velero.io/apiservice", newAPIServiceRestoreItemAction).
Serve()
},
}
@@ -197,3 +198,7 @@ func newChangePVCNodeSelectorItemAction(f client.Factory) veleroplugin.HandlerIn
), nil
}
}
func newAPIServiceRestoreItemAction(logger logrus.FieldLogger) (interface{}, error) {
return restore.NewAPIServiceAction(logger), nil
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2018, 2019, 2020 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -23,11 +23,13 @@ import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/vmware-tanzu/velero/internal/velero"
)
func DaemonSet(namespace string, opts ...podTemplateOption) *appsv1.DaemonSet {
c := &podTemplateConfig{
image: DefaultImage,
image: velero.DefaultVeleroImage(),
}
for _, opt := range opts {

View File

@@ -1,5 +1,5 @@
/*
Copyright 2020 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/vmware-tanzu/velero/internal/velero"
"github.com/vmware-tanzu/velero/pkg/builder"
)
@@ -117,7 +118,7 @@ func WithDefaultVolumesToRestic() podTemplateOption {
func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment {
// TODO: Add support for server args
c := &podTemplateConfig{
image: DefaultImage,
image: velero.DefaultVeleroImage(),
}
for _, opt := range opts {

View File

@@ -1,5 +1,5 @@
/*
Copyright 2019 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@ limitations under the License.
package install
import (
"context"
"fmt"
"io"
"strings"
@@ -25,6 +26,7 @@ import (
"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -32,6 +34,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/vmware-tanzu/velero/pkg/client"
"github.com/vmware-tanzu/velero/pkg/util/kube"
@@ -57,53 +60,95 @@ type ResourceGroup struct {
OtherResources []*unstructured.Unstructured
}
// crdsAreReady polls the API server to see if the BackupStorageLocation and VolumeSnapshotLocation CRDs are ready to create objects.
func crdsAreReady(factory client.DynamicFactory, crdKinds []string) (bool, error) {
gvk := schema.FromAPIVersionAndKind(apiextv1beta1.SchemeGroupVersion.String(), "CustomResourceDefinition")
apiResource := metav1.APIResource{
Name: kindToResource["CustomResourceDefinition"],
Namespaced: false,
}
c, err := factory.ClientForGroupVersionResource(gvk.GroupVersion(), apiResource, "")
if err != nil {
return false, errors.Wrapf(err, "Error creating client for CustomResourceDefinition polling")
}
// Track all the CRDs that have been found and successfully marshalled.
// len should be equal to len(crdKinds) in the happy path.
foundCRDs := make([]*apiextv1beta1.CustomResourceDefinition, 0)
var areReady bool
err = wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
for _, k := range crdKinds {
unstruct, err := c.Get(k, metav1.GetOptions{})
// crdV1Beta1ReadinessFn returns a function that can be used for polling to check
// if the provided unstructured v1beta1 CRDs are ready for use in the cluster.
func crdV1Beta1ReadinessFn(kbClient kbclient.Client, unstructuredCrds []*unstructured.Unstructured) func() (bool, error) {
// Track all the CRDs that have been found and in ready state.
// len should be equal to len(unstructuredCrds) in the happy path.
return func() (bool, error) {
foundCRDs := make([]*apiextv1beta1.CustomResourceDefinition, 0)
for _, unstructuredCrd := range unstructuredCrds {
crd := &apiextv1beta1.CustomResourceDefinition{}
key := kbclient.ObjectKey{Name: unstructuredCrd.GetName()}
err := kbClient.Get(context.Background(), key, crd)
if apierrors.IsNotFound(err) {
return false, nil
} else if err != nil {
return false, errors.Wrapf(err, "error waiting for %s to be ready", k)
return false, errors.Wrapf(err, "error waiting for %s to be ready", crd.GetName())
}
crd := new(apiextv1beta1.CustomResourceDefinition)
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstruct.Object, crd); err != nil {
return false, errors.Wrapf(err, "error converting %s from unstructured", k)
}
foundCRDs = append(foundCRDs, crd)
}
if len(foundCRDs) != len(crdKinds) {
if len(foundCRDs) != len(unstructuredCrds) {
return false, nil
}
for _, crd := range foundCRDs {
if !kube.IsCRDReady(crd) {
ready := kube.IsV1Beta1CRDReady(crd)
if !ready {
return false, nil
}
}
areReady = true
return true, nil
})
return areReady, nil
}
}
// crdV1ReadinessFn returns a function that can be used for polling to check
// if the provided unstructured v1 CRDs are ready for use in the cluster.
func crdV1ReadinessFn(kbClient kbclient.Client, unstructuredCrds []*unstructured.Unstructured) func() (bool, error) {
return func() (bool, error) {
foundCRDs := make([]*apiextv1.CustomResourceDefinition, 0)
for _, unstructuredCrd := range unstructuredCrds {
crd := &apiextv1.CustomResourceDefinition{}
key := kbclient.ObjectKey{Name: unstructuredCrd.GetName()}
err := kbClient.Get(context.Background(), key, crd)
if apierrors.IsNotFound(err) {
return false, nil
} else if err != nil {
return false, errors.Wrapf(err, "error waiting for %s to be ready", crd.GetName())
}
foundCRDs = append(foundCRDs, crd)
}
if len(foundCRDs) != len(unstructuredCrds) {
return false, nil
}
for _, crd := range foundCRDs {
ready := kube.IsV1CRDReady(crd)
if !ready {
return false, nil
}
}
return true, nil
}
}
// crdsAreReady polls the API server to see if the Velero CRDs are ready to create objects.
func crdsAreReady(kbClient kbclient.Client, crds []*unstructured.Unstructured) (bool, error) {
if len(crds) == 0 {
// no CRDs to check so return
return true, nil
}
// We assume that all Velero CRDs have the same GVK so we can use the GVK of the
// first CRD to determine whether to use the v1beta1 or v1 API during polling.
gvk := crds[0].GroupVersionKind()
var crdReadinessFn func() (bool, error)
if gvk.Version == "v1beta1" {
crdReadinessFn = crdV1Beta1ReadinessFn(kbClient, crds)
} else if gvk.Version == "v1" {
crdReadinessFn = crdV1ReadinessFn(kbClient, crds)
} else {
return false, fmt.Errorf("unsupported CRD version %q", gvk.Version)
}
err := wait.PollImmediate(time.Second, time.Minute, crdReadinessFn)
if err != nil {
return false, errors.Wrap(err, "Error polling for CRDs")
}
return true, nil
}
func isAvailable(c appsv1.DeploymentCondition) bool {
@@ -282,19 +327,19 @@ func CreateClient(r *unstructured.Unstructured, factory client.DynamicFactory, w
// Resources will be sorted into CustomResourceDefinitions and any other resource type, and the function will wait up to 1 minute
// for CRDs to be ready before proceeding.
// An io.Writer can be used to output to a log or the console.
func Install(factory client.DynamicFactory, resources *unstructured.UnstructuredList, w io.Writer) error {
func Install(dynamicFactory client.DynamicFactory, kbClient kbclient.Client, resources *unstructured.UnstructuredList, w io.Writer) error {
rg := GroupResources(resources)
//Install CRDs first
for _, r := range rg.CRDResources {
if err := createResource(r, factory, w); err != nil {
if err := createResource(r, dynamicFactory, w); err != nil {
return err
}
}
// Wait for CRDs to be ready before proceeding
fmt.Fprint(w, "Waiting for resources to be ready in cluster...\n")
_, err := crdsAreReady(factory, []string{"backupstoragelocations.velero.io", "volumesnapshotlocations.velero.io"})
_, err := crdsAreReady(kbClient, rg.CRDResources)
if err == wait.ErrWaitTimeout {
return errors.Errorf("timeout reached, CRDs not ready")
} else if err != nil {
@@ -303,7 +348,7 @@ func Install(factory client.DynamicFactory, resources *unstructured.Unstructured
// Install all other resources
for _, r := range rg.OtherResources {
if err = createResource(r, factory, w); err != nil {
if err = createResource(r, dynamicFactory, w); err != nil {
return err
}
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2020 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -19,30 +19,19 @@ package install
import (
"time"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
corev1 "k8s.io/api/core/v1"
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/vmware-tanzu/velero/config/crd/crds"
"github.com/vmware-tanzu/velero/pkg/buildinfo"
v1crds "github.com/vmware-tanzu/velero/config/crd/v1/crds"
v1beta1crds "github.com/vmware-tanzu/velero/config/crd/v1beta1/crds"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
)
// Use "latest" if the build process didn't supply a version
func imageVersion() string {
if buildinfo.Version == "" {
return "latest"
}
return buildinfo.Version
}
// DefaultImage is the default image to use for the Velero deployment and restic daemonset containers.
var (
DefaultImage = "velero/velero:" + imageVersion()
DefaultVeleroPodCPURequest = "500m"
DefaultVeleroPodMemRequest = "128Mi"
DefaultVeleroPodCPULimit = "1000m"
@@ -105,25 +94,25 @@ func ServiceAccount(namespace string, annotations map[string]string) *corev1.Ser
}
}
func ClusterRoleBinding(namespace string) *rbacv1beta1.ClusterRoleBinding {
func ClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding {
crbName := "velero"
if namespace != DefaultVeleroNamespace {
crbName = "velero-" + namespace
}
crb := &rbacv1beta1.ClusterRoleBinding{
crb := &rbacv1.ClusterRoleBinding{
ObjectMeta: objectMeta("", crbName),
TypeMeta: metav1.TypeMeta{
Kind: "ClusterRoleBinding",
APIVersion: rbacv1beta1.SchemeGroupVersion.String(),
APIVersion: rbacv1.SchemeGroupVersion.String(),
},
Subjects: []rbacv1beta1.Subject{
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Namespace: namespace,
Name: "velero",
},
},
RoleRef: rbacv1beta1.RoleRef{
RoleRef: rbacv1.RoleRef{
Kind: "ClusterRole",
Name: "cluster-admin",
APIGroup: "rbac.authorization.k8s.io",
@@ -227,17 +216,26 @@ type VeleroOptions struct {
NoDefaultBackupLocation bool
CACertData []byte
Features []string
CRDsVersion string
DefaultVolumesToRestic bool
}
func AllCRDs() *unstructured.UnstructuredList {
func AllCRDs(perferredAPIVersion string) *unstructured.UnstructuredList {
resources := new(unstructured.UnstructuredList)
// Set the GVK so that the serialization framework outputs the list properly
resources.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "List"})
for _, crd := range crds.CRDs {
crd.SetLabels(Labels())
appendUnstructured(resources, crd)
switch perferredAPIVersion {
case "v1beta1":
for _, crd := range v1beta1crds.CRDs {
crd.SetLabels(Labels())
appendUnstructured(resources, crd)
}
case "v1":
for _, crd := range v1crds.CRDs {
crd.SetLabels(Labels())
appendUnstructured(resources, crd)
}
}
return resources
@@ -245,8 +243,8 @@ func AllCRDs() *unstructured.UnstructuredList {
// AllResources returns a list of all resources necessary to install Velero, in the appropriate order, into a Kubernetes cluster.
// Items are unstructured, since there are different data types returned.
func AllResources(o *VeleroOptions) (*unstructured.UnstructuredList, error) {
resources := AllCRDs()
func AllResources(o *VeleroOptions) *unstructured.UnstructuredList {
resources := AllCRDs(o.CRDsVersion)
ns := Namespace(o.Namespace)
appendUnstructured(resources, ns)
@@ -317,5 +315,5 @@ func AllResources(o *VeleroOptions) (*unstructured.UnstructuredList, error) {
appendUnstructured(resources, ds)
}
return resources, nil
return resources
}

View File

@@ -100,9 +100,20 @@ func isPVBMatchPod(pvb *velerov1api.PodVolumeBackup, podName string, namespace s
return podName == pvb.Spec.Pod.Name && namespace == pvb.Spec.Pod.Namespace
}
// volumeIsProjected checks if the given volume exists in the list of podVolumes
// and returns true if the volume has a projected source
func volumeIsProjected(volumeName string, podVolumes []corev1api.Volume) bool {
for _, volume := range podVolumes {
if volume.Name == volumeName && volume.Projected != nil {
return true
}
}
return false
}
// GetVolumeBackupsForPod returns a map, of volume name -> snapshot id,
// of the PodVolumeBackups that exist for the provided pod.
func GetVolumeBackupsForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod metav1.Object, sourcePodNs string) map[string]string {
func GetVolumeBackupsForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod *corev1api.Pod, sourcePodNs string) map[string]string {
volumes := make(map[string]string)
for _, pvb := range podVolumeBackups {
@@ -116,6 +127,13 @@ func GetVolumeBackupsForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod
continue
}
// If the volume came from a projected source, skip its restore.
// This allows backups affected by https://github.com/vmware-tanzu/velero/issues/3863
// to be restored successfully.
if volumeIsProjected(pvb.Spec.Volume, pod.Spec.Volumes) {
continue
}
volumes[pvb.Spec.Volume] = pvb.Status.SnapshotID
}
@@ -183,6 +201,10 @@ func GetPodVolumesUsingRestic(pod *corev1api.Pod, defaultVolumesToRestic bool) [
if pv.ConfigMap != nil {
continue
}
// don't backup volumes mounted as projected volumes, all data in those come from kube state.
if pv.Projected != nil {
continue
}
// don't backup volumes that are included in the exclude list.
if contains(volsToExclude, pv.Name) {
continue

View File

@@ -37,6 +37,7 @@ func TestGetVolumeBackupsForPod(t *testing.T) {
tests := []struct {
name string
podVolumeBackups []*velerov1api.PodVolumeBackup
podVolumes []corev1api.Volume
podAnnotations map[string]string
podName string
sourcePodNs string
@@ -127,6 +128,30 @@ func TestGetVolumeBackupsForPod(t *testing.T) {
sourcePodNs: "TestNS",
expected: map[string]string{"pvbtest1-foo": "snapshot1"},
},
{
name: "volumes from PVBs that correspond to a pod volume from a projected source are not returned",
podVolumeBackups: []*velerov1api.PodVolumeBackup{
builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvb-non-projected").Result(),
builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvb-projected").Result(),
},
podVolumes: []corev1api.Volume{
{
Name: "pvb-non-projected",
VolumeSource: corev1api.VolumeSource{
PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{},
},
},
{
Name: "pvb-projected",
VolumeSource: corev1api.VolumeSource{
Projected: &corev1api.ProjectedVolumeSource{},
},
},
},
podName: "TestPod",
sourcePodNs: "TestNS",
expected: map[string]string{"pvb-non-projected": "snapshot1"},
},
}
for _, test := range tests {
@@ -134,6 +159,7 @@ func TestGetVolumeBackupsForPod(t *testing.T) {
pod := &corev1api.Pod{}
pod.Annotations = test.podAnnotations
pod.Name = test.podName
pod.Spec.Volumes = test.podVolumes
res := GetVolumeBackupsForPod(test.podVolumeBackups, pod, test.sourcePodNs)
assert.Equal(t, test.expected, res)
@@ -507,6 +533,41 @@ func TestGetPodVolumesUsingRestic(t *testing.T) {
},
expected: []string{"resticPV1", "resticPV2", "resticPV3"},
},
{
name: "should exclude projected volumes",
defaultVolumesToRestic: true,
pod: &corev1api.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3",
},
},
Spec: corev1api.PodSpec{
Volumes: []corev1api.Volume{
{Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"},
{
Name: "projected",
VolumeSource: corev1api.VolumeSource{
Projected: &corev1api.ProjectedVolumeSource{
Sources: []corev1api.VolumeProjection{{
Secret: &corev1api.SecretProjection{
LocalObjectReference: corev1api.LocalObjectReference{},
Items: nil,
Optional: nil,
},
DownwardAPI: nil,
ConfigMap: nil,
ServiceAccountToken: nil,
}},
DefaultMode: nil,
},
},
},
},
},
},
expected: []string{"resticPV1", "resticPV2", "resticPV3"},
},
}
for _, tc := range testCases {
@@ -594,3 +655,78 @@ func TestIsPVBMatchPod(t *testing.T) {
}
}
func TestVolumeIsProjected(t *testing.T) {
testCases := []struct {
name string
volumeName string
podVolumes []corev1api.Volume
expected bool
}{
{
name: "volume name not in list of volumes",
volumeName: "missing-volume",
podVolumes: []corev1api.Volume{
{
Name: "non-projected",
VolumeSource: corev1api.VolumeSource{
PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{},
},
},
{
Name: "projected",
VolumeSource: corev1api.VolumeSource{
Projected: &corev1api.ProjectedVolumeSource{},
},
},
},
expected: false,
},
{
name: "volume name in list of volumes but not projected",
volumeName: "non-projected",
podVolumes: []corev1api.Volume{
{
Name: "non-projected",
VolumeSource: corev1api.VolumeSource{
PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{},
},
},
{
Name: "projected",
VolumeSource: corev1api.VolumeSource{
Projected: &corev1api.ProjectedVolumeSource{},
},
},
},
expected: false,
},
{
name: "volume name in list of volumes and projected",
volumeName: "projected",
podVolumes: []corev1api.Volume{
{
Name: "non-projected",
VolumeSource: corev1api.VolumeSource{
PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{},
},
},
{
Name: "projected",
VolumeSource: corev1api.VolumeSource{
Projected: &corev1api.ProjectedVolumeSource{},
},
},
},
expected: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actual := volumeIsProjected(tc.volumeName, tc.podVolumes)
assert.Equal(t, tc.expected, actual)
})
}
}

View File

@@ -0,0 +1,51 @@
/*
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 restore
import (
"github.com/sirupsen/logrus"
"k8s.io/kube-aggregator/pkg/controllers/autoregister"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
)
type APIServiceAction struct {
logger logrus.FieldLogger
}
// NewAPIServiceAction returns an APIServiceAction which is a RestoreItemAction plugin
// that will skip the restore of any APIServices which are managed by Kubernetes. This
// is determined by looking for the "kube-aggregator.kubernetes.io/automanaged" label on
// the APIService.
func NewAPIServiceAction(logger logrus.FieldLogger) *APIServiceAction {
return &APIServiceAction{logger: logger}
}
func (a *APIServiceAction) AppliesTo() (velero.ResourceSelector, error) {
return velero.ResourceSelector{
IncludedResources: []string{"apiservices"},
LabelSelector: autoregister.AutoRegisterManagedLabel,
}, nil
}
func (a *APIServiceAction) Execute(input *velero.RestoreItemActionExecuteInput) (*velero.RestoreItemActionExecuteOutput, error) {
a.logger.Info("Executing APIServiceAction")
defer a.logger.Info("Done executing APIServiceAction")
a.logger.Infof("Skipping restore of APIService as it is managed by Kubernetes")
return velero.NewRestoreItemActionExecuteOutput(input.Item).WithoutRestore(), nil
}

View File

@@ -0,0 +1,53 @@
/*
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package restore
import (
"testing"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
)
func TestAPIServiceActionExecuteSkipsRestore(t *testing.T) {
obj := apiregistrationv1.APIService{
ObjectMeta: metav1.ObjectMeta{
Name: "v1.test.velero.io",
},
}
unstructuredAPIService, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&obj)
require.NoError(t, err)
action := NewAPIServiceAction(velerotest.NewLogger())
res, err := action.Execute(&velero.RestoreItemActionExecuteInput{
Item: &unstructured.Unstructured{Object: unstructuredAPIService},
ItemFromBackup: &unstructured.Unstructured{Object: unstructuredAPIService},
})
require.NoError(t, err)
var apiService apiregistrationv1.APIService
require.NoError(t, runtime.DefaultUnstructuredConverter.FromUnstructured(res.UpdatedItem.UnstructuredContent(), &apiService))
require.Equal(t, obj, apiService)
require.Equal(t, true, res.SkipRestore)
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2018, 2019, 2020 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -29,9 +29,9 @@ import (
"k8s.io/apimachinery/pkg/runtime"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
veleroimage "github.com/vmware-tanzu/velero/internal/velero"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
"github.com/vmware-tanzu/velero/pkg/buildinfo"
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
"github.com/vmware-tanzu/velero/pkg/label"
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
@@ -41,7 +41,6 @@ import (
)
const (
defaultImageBase = "velero/velero-restic-restore-helper"
defaultCPURequestLimit = "100m"
defaultMemRequestLimit = "128Mi"
defaultCommand = "/velero-restic-restore-helper"
@@ -193,13 +192,13 @@ func getCommand(log logrus.FieldLogger, config *corev1.ConfigMap) []string {
func getImage(log logrus.FieldLogger, config *corev1.ConfigMap) string {
if config == nil {
log.Debug("No config found for plugin")
return initContainerImage(defaultImageBase)
return veleroimage.DefaultResticRestoreHelperImage()
}
image := config.Data["image"]
if image == "" {
log.Debugf("No custom image configured")
return initContainerImage(defaultImageBase)
return veleroimage.DefaultResticRestoreHelperImage()
}
log = log.WithField("image", image)
@@ -207,15 +206,17 @@ func getImage(log logrus.FieldLogger, config *corev1.ConfigMap) string {
parts := strings.Split(image, "/")
if len(parts) == 1 {
defaultImage := veleroimage.DefaultResticRestoreHelperImage()
// Image supplied without registry part
log.Debugf("Plugin config contains image name without registry name. Return defaultImageBase")
return initContainerImage(defaultImageBase)
log.Infof("Plugin config contains image name without registry name. Using default init container image: %q", defaultImage)
return defaultImage
}
if !(strings.Contains(parts[len(parts)-1], ":")) {
// tag-less image name: add tag
log.Debugf("Plugin config contains image name without tag. Adding tag.")
return initContainerImage(image)
tag := veleroimage.ImageTag()
// tag-less image name: add default image tag for this version of Velero
log.Infof("Plugin config contains image name without tag. Adding tag: %q", tag)
return fmt.Sprintf("%s:%s", image, tag)
} else {
// tagged image name
log.Debugf("Plugin config contains image name with tag")
@@ -306,12 +307,3 @@ func newResticInitContainerBuilder(image, restoreUID string) *builder.ContainerB
},
}...)
}
func initContainerImage(imageBase string) string {
tag := buildinfo.Version
if tag == "" {
tag = "latest"
}
return fmt.Sprintf("%s:%s", imageBase, tag)
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2019 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package restore
import (
"context"
"fmt"
"sort"
"testing"
@@ -31,6 +30,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
veleroimage "github.com/vmware-tanzu/velero/internal/velero"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
"github.com/vmware-tanzu/velero/pkg/buildinfo"
@@ -49,41 +49,40 @@ func TestGetImage(t *testing.T) {
}
}
originalVersion := buildinfo.Version
buildinfo.Version = "buildinfo-version"
defer func() {
buildinfo.Version = originalVersion
}()
defaultImage := veleroimage.DefaultResticRestoreHelperImage()
tests := []struct {
name string
configMap *corev1api.ConfigMap
want string
name string
configMap *corev1api.ConfigMap
buildInfoVersion string
want string
}{
{
name: "nil config map returns default image with buildinfo.Version as tag",
name: "nil config map returns default image",
configMap: nil,
want: fmt.Sprintf("%s:%s", defaultImageBase, buildinfo.Version),
want: defaultImage,
},
{
name: "config map without 'image' key returns default image with buildinfo.Version as tag",
name: "config map without 'image' key returns default image",
configMap: configMapWithData("non-matching-key", "val"),
want: fmt.Sprintf("%s:%s", defaultImageBase, buildinfo.Version),
want: defaultImage,
},
{
name: "config map without '/' in image name returns default image with buildinfo.Version as tag",
name: "config map without '/' in image name returns default image",
configMap: configMapWithData("image", "my-image"),
want: fmt.Sprintf("%s:%s", defaultImageBase, buildinfo.Version),
want: defaultImage,
},
{
name: "config map with untagged image returns image with buildinfo.Version as tag",
configMap: configMapWithData("image", "myregistry.io/my-image"),
want: fmt.Sprintf("%s:%s", "myregistry.io/my-image", buildinfo.Version),
name: "config map with untagged image returns image with buildinfo.Version as tag",
configMap: configMapWithData("image", "myregistry.io/my-image"),
buildInfoVersion: "buildinfo-version",
want: "myregistry.io/my-image:buildinfo-version",
},
{
name: "config map with untagged image and custom registry port with ':' returns image with buildinfo.Version as tag",
configMap: configMapWithData("image", "myregistry.io:34567/my-image"),
want: fmt.Sprintf("%s:%s", "myregistry.io:34567/my-image", buildinfo.Version),
name: "config map with untagged image and custom registry port with ':' returns image with buildinfo.Version as tag",
configMap: configMapWithData("image", "myregistry.io:34567/my-image"),
buildInfoVersion: "buildinfo-version",
want: "myregistry.io:34567/my-image:buildinfo-version",
},
{
name: "config map with tagged image returns tagged image",
@@ -99,6 +98,13 @@ func TestGetImage(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if test.buildInfoVersion != "" {
originalVersion := buildinfo.Version
buildinfo.Version = test.buildInfoVersion
defer func() {
buildinfo.Version = originalVersion
}()
}
assert.Equal(t, test.want, getImage(velerotest.NewLogger(), test.configMap))
})
}
@@ -119,6 +125,8 @@ func TestResticRestoreActionExecute(t *testing.T) {
veleroNs = "velero"
)
defaultResticRestoreHelperImage := veleroimage.DefaultResticRestoreHelperImage()
tests := []struct {
name string
pod *corev1api.Pod
@@ -135,7 +143,7 @@ func TestResticRestoreActionExecute(t *testing.T) {
ObjectMeta(
builder.WithAnnotations("snapshot.velero.io/myvol", "")).
InitContainers(
newResticInitContainerBuilder(initContainerImage(defaultImageBase), "").
newResticInitContainerBuilder(defaultResticRestoreHelperImage, "").
Resources(&resourceReqs).
SecurityContext(&securityContext).
VolumeMounts(builder.ForVolumeMount("myvol", "/restores/myvol").Result()).
@@ -152,7 +160,7 @@ func TestResticRestoreActionExecute(t *testing.T) {
ObjectMeta(
builder.WithAnnotations("snapshot.velero.io/myvol", "")).
InitContainers(
newResticInitContainerBuilder(initContainerImage(defaultImageBase), "").
newResticInitContainerBuilder(defaultResticRestoreHelperImage, "").
Resources(&resourceReqs).
SecurityContext(&securityContext).
VolumeMounts(builder.ForVolumeMount("myvol", "/restores/myvol").Result()).
@@ -195,7 +203,7 @@ func TestResticRestoreActionExecute(t *testing.T) {
ObjectMeta(
builder.WithAnnotations("snapshot.velero.io/not-used", "")).
InitContainers(
newResticInitContainerBuilder(initContainerImage(defaultImageBase), "").
newResticInitContainerBuilder(defaultResticRestoreHelperImage, "").
Resources(&resourceReqs).
SecurityContext(&securityContext).
VolumeMounts(builder.ForVolumeMount("vol-1", "/restores/vol-1").Result(), builder.ForVolumeMount("vol-2", "/restores/vol-2").Result()).
@@ -239,7 +247,7 @@ func TestResticRestoreActionExecute(t *testing.T) {
builder.ForVolume("vol-2").PersistentVolumeClaimSource("pvc-2").Result(),
).
InitContainers(
newResticInitContainerBuilder(initContainerImage(defaultImageBase), "").
newResticInitContainerBuilder(defaultResticRestoreHelperImage, "").
Resources(&resourceReqs).
SecurityContext(&securityContext).
VolumeMounts(builder.ForVolumeMount("vol-1", "/restores/vol-1").Result(), builder.ForVolumeMount("vol-2", "/restores/vol-2").Result()).

View File

@@ -377,6 +377,10 @@ func getOrderedResources(resourcePriorities []string, backupResources map[string
return append(resourcePriorities, orderedBackupResources...)
}
type progressUpdate struct {
totalItems, itemsRestored int
}
func (ctx *restoreContext) execute() (Result, Result) {
warnings, errs := Result{}, Result{}
@@ -409,14 +413,6 @@ func (ctx *restoreContext) execute() (Result, Result) {
}
}
selectedResourceCollection, w, e := ctx.getOrderedResourceCollection(backupResources)
warnings.Merge(&w)
errs.Merge(&e)
type progressUpdate struct {
totalItems, itemsRestored int
}
update := make(chan progressUpdate)
quit := make(chan struct{})
@@ -456,94 +452,69 @@ func (ctx *restoreContext) execute() (Result, Result) {
}()
// totalItems: previously discovered items, i: iteration counter.
totalItems, i, existingNamespaces := 0, 0, sets.NewString()
totalItems, processedItems, existingNamespaces := 0, 0, sets.NewString()
// First restore CRDs. This is needed so that they are available in the cluster
// when getOrderedResourceCollection is called again on the whole backup and
// needs to validate all resources listed.
crdResourceCollection, processedResources, w, e := ctx.getOrderedResourceCollection(
backupResources,
make([]restoreableResource, 0),
sets.NewString(),
[]string{"customresourcedefinitions"},
false,
)
warnings.Merge(&w)
errs.Merge(&e)
for _, selectedResource := range crdResourceCollection {
totalItems += selectedResource.totalItems
}
for _, selectedResource := range crdResourceCollection {
var w, e Result
// Restore this resource
processedItems, w, e = ctx.processSelectedResource(
selectedResource,
totalItems,
processedItems,
existingNamespaces,
update,
)
warnings.Merge(&w)
errs.Merge(&e)
}
// Restore everything else
selectedResourceCollection, _, w, e := ctx.getOrderedResourceCollection(
backupResources,
crdResourceCollection,
processedResources,
ctx.resourcePriorities,
true,
)
warnings.Merge(&w)
errs.Merge(&e)
// reset processedItems and totalItems before processing full resource list
processedItems = 0
totalItems = 0
for _, selectedResource := range selectedResourceCollection {
totalItems += selectedResource.totalItems
}
for _, selectedResource := range selectedResourceCollection {
groupResource := schema.ParseGroupResource(selectedResource.resource)
for namespace, selectedItems := range selectedResource.selectedItemsByNamespace {
for _, selectedItem := range selectedItems {
// If we don't know whether this namespace exists yet, attempt to create
// it in order to ensure it exists. Try to get it from the backup tarball
// (in order to get any backed-up metadata), but if we don't find it there,
// create a blank one.
if namespace != "" && !existingNamespaces.Has(selectedItem.targetNamespace) {
logger := ctx.log.WithField("namespace", namespace)
ns := getNamespace(
logger,
archive.GetItemFilePath(ctx.restoreDir, "namespaces", "", namespace),
selectedItem.targetNamespace,
)
_, nsCreated, err := kube.EnsureNamespaceExistsAndIsReady(
ns,
ctx.namespaceClient,
ctx.resourceTerminatingTimeout,
)
if err != nil {
errs.AddVeleroError(err)
continue
}
// Add the newly created namespace to the list of restored items.
if nsCreated {
itemKey := velero.ResourceIdentifier{
GroupResource: kuberesource.Namespaces,
Namespace: ns.Namespace,
Name: ns.Name,
}
ctx.restoredItems[itemKey] = struct{}{}
}
// Keep track of namespaces that we know exist so we don't
// have to try to create them multiple times.
existingNamespaces.Insert(selectedItem.targetNamespace)
}
obj, err := archive.Unmarshal(ctx.fileSystem, selectedItem.path)
if err != nil {
errs.Add(
selectedItem.targetNamespace,
fmt.Errorf(
"error decoding %q: %v",
strings.Replace(selectedItem.path, ctx.restoreDir+"/", "", -1),
err,
),
)
continue
}
w, e := ctx.restoreItem(obj, groupResource, selectedItem.targetNamespace)
warnings.Merge(&w)
errs.Merge(&e)
i++
// totalItems keeps the count of items previously known. There
// may be additional items restored by plugins. We want to include
// the additional items by looking at restoredItems at the same
// time, we don't want previously known items counted twice as
// they are present in both restoredItems and totalItems.
actualTotalItems := len(ctx.restoredItems) + (totalItems - i)
update <- progressUpdate{
totalItems: actualTotalItems,
itemsRestored: len(ctx.restoredItems),
}
}
}
// If we just restored custom resource definitions (CRDs), refresh
// discovery because the restored CRDs may have created new APIs that
// didn't previously exist in the cluster, and we want to be able to
// resolve & restore instances of them in subsequent loop iterations.
if groupResource == kuberesource.CustomResourceDefinitions {
if err := ctx.discoveryHelper.Refresh(); err != nil {
warnings.Add("", errors.Wrap(err, "refresh discovery after restoring CRDs"))
}
}
var w, e Result
// Restore this resource
processedItems, w, e = ctx.processSelectedResource(
selectedResource,
totalItems,
processedItems,
existingNamespaces,
update,
)
warnings.Merge(&w)
errs.Merge(&e)
}
// Close the progress update channel.
@@ -605,6 +576,107 @@ func (ctx *restoreContext) execute() (Result, Result) {
return warnings, errs
}
// Process and restore one restoreableResource from the backup and update restore progress
// metadata. At this point, the resource has already been validated and counted for inclusion
// in the expected total restore count.
func (ctx *restoreContext) processSelectedResource(
selectedResource restoreableResource,
totalItems int,
processedItems int,
existingNamespaces sets.String,
update chan progressUpdate,
) (int, Result, Result) {
warnings, errs := Result{}, Result{}
groupResource := schema.ParseGroupResource(selectedResource.resource)
for namespace, selectedItems := range selectedResource.selectedItemsByNamespace {
for _, selectedItem := range selectedItems {
// If we don't know whether this namespace exists yet, attempt to create
// it in order to ensure it exists. Try to get it from the backup tarball
// (in order to get any backed-up metadata), but if we don't find it there,
// create a blank one.
if namespace != "" && !existingNamespaces.Has(selectedItem.targetNamespace) {
logger := ctx.log.WithField("namespace", namespace)
ns := getNamespace(
logger,
archive.GetItemFilePath(ctx.restoreDir, "namespaces", "", namespace),
selectedItem.targetNamespace,
)
_, nsCreated, err := kube.EnsureNamespaceExistsAndIsReady(
ns,
ctx.namespaceClient,
ctx.resourceTerminatingTimeout,
)
if err != nil {
errs.AddVeleroError(err)
continue
}
// Add the newly created namespace to the list of restored items.
if nsCreated {
itemKey := velero.ResourceIdentifier{
GroupResource: kuberesource.Namespaces,
Namespace: ns.Namespace,
Name: ns.Name,
}
ctx.restoredItems[itemKey] = struct{}{}
}
// Keep track of namespaces that we know exist so we don't
// have to try to create them multiple times.
existingNamespaces.Insert(selectedItem.targetNamespace)
}
obj, err := archive.Unmarshal(ctx.fileSystem, selectedItem.path)
if err != nil {
errs.Add(
selectedItem.targetNamespace,
fmt.Errorf(
"error decoding %q: %v",
strings.Replace(selectedItem.path, ctx.restoreDir+"/", "", -1),
err,
),
)
continue
}
w, e := ctx.restoreItem(obj, groupResource, selectedItem.targetNamespace)
warnings.Merge(&w)
errs.Merge(&e)
processedItems++
// totalItems keeps the count of items previously known. There
// may be additional items restored by plugins. We want to include
// the additional items by looking at restoredItems at the same
// time, we don't want previously known items counted twice as
// they are present in both restoredItems and totalItems.
actualTotalItems := len(ctx.restoredItems) + (totalItems - processedItems)
update <- progressUpdate{
totalItems: actualTotalItems,
itemsRestored: len(ctx.restoredItems),
}
ctx.log.WithFields(map[string]interface{}{
"progress": "",
"resource": groupResource.String(),
"namespace": selectedItem.targetNamespace,
"name": selectedItem.name,
}).Infof("Restored %d items out of an estimated total of %d (estimate will change throughout the restore)", len(ctx.restoredItems), actualTotalItems)
}
}
// If we just restored custom resource definitions (CRDs), refresh
// discovery because the restored CRDs may have created new APIs that
// didn't previously exist in the cluster, and we want to be able to
// resolve & restore instances of them in subsequent loop iterations.
if groupResource == kuberesource.CustomResourceDefinitions {
if err := ctx.discoveryHelper.Refresh(); err != nil {
warnings.Add("", errors.Wrap(err, "refresh discovery after restoring CRDs"))
}
}
return processedItems, warnings, errs
}
// getNamespace returns a namespace API object that we should attempt to
// create before restoring anything into it. It will come from the backup
// tarball if it exists, else will be a new one. If from the tarball, it
@@ -1232,8 +1304,16 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso
return warnings, errs
}
if groupResource == kuberesource.Pods && len(restic.GetVolumeBackupsForPod(ctx.podVolumeBackups, obj, originalNamespace)) > 0 {
restorePodVolumeBackups(ctx, createdObj, originalNamespace)
if groupResource == kuberesource.Pods {
pod := new(v1.Pod)
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), pod); err != nil {
errs.Add(namespace, err)
return warnings, errs
}
if len(restic.GetVolumeBackupsForPod(ctx.podVolumeBackups, pod, originalNamespace)) > 0 {
restorePodVolumeBackups(ctx, createdObj, originalNamespace)
}
}
if groupResource == kuberesource.Pods {
@@ -1559,10 +1639,14 @@ type restoreableItem struct {
// identifiers, applies resource include/exclude criteria, and Kubernetes
// selectors to make a list of resources to be actually restored preserving the
// original order.
func (ctx *restoreContext) getOrderedResourceCollection(backupResources map[string]*archive.ResourceItems) ([]restoreableResource, Result, Result) {
func (ctx *restoreContext) getOrderedResourceCollection(
backupResources map[string]*archive.ResourceItems,
restoreResourceCollection []restoreableResource,
processedResources sets.String,
resourcePriorities []string,
includeAllResources bool,
) ([]restoreableResource, sets.String, Result, Result) {
var warnings, errs Result
processedResources := sets.NewString()
restoreResourceCollection := make([]restoreableResource, 0)
// Iterate through an ordered list of resources to restore, checking each
// one to see if it should be restored. Note that resources *may* be in this
// list twice, i.e. once due to being a prioritized resource, and once due
@@ -1577,7 +1661,13 @@ func (ctx *restoreContext) getOrderedResourceCollection(backupResources map[stri
// Since we keep track of the fully-resolved group-resources that we *have*
// restored, we won't try to restore a resource twice even if it's in the
// ordered list twice.
for _, resource := range getOrderedResources(ctx.resourcePriorities, backupResources) {
var resourceList []string
if includeAllResources {
resourceList = getOrderedResources(resourcePriorities, backupResources)
} else {
resourceList = resourcePriorities
}
for _, resource := range resourceList {
// try to resolve the resource via discovery to a complete group/version/resource
gvr, _, err := ctx.discoveryHelper.ResourceFor(schema.ParseGroupResource(resource).WithVersion(""))
if err != nil {
@@ -1650,7 +1740,7 @@ func (ctx *restoreContext) getOrderedResourceCollection(backupResources map[stri
// record that we've restored the resource
processedResources.Insert(groupResource.String())
}
return restoreResourceCollection, warnings, errs
return restoreResourceCollection, processedResources, warnings, errs
}
// getSelectedRestoreableItems applies Kubernetes selectors on individual items

View File

@@ -1,5 +1,5 @@
/*
Copyright 2019 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -1139,6 +1139,22 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) {
new(recordResourcesAction).ForNamespace("ns-1").ForResource("pods"): {"ns-1/pod-1"},
},
},
{
name: "single action with a resource and label selector runs only for resources matching that label",
restore: defaultRestore().Result(),
backup: defaultBackup().Result(),
tarball: test.NewTarWriter(t).
AddItems("pods",
builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("restore-resource", "true")).Result(),
builder.ForPod("ns-1", "pod-2").ObjectMeta(builder.WithLabels("do-not-restore-resource", "true")).Result(),
builder.ForPod("ns-2", "pod-1").Result(),
builder.ForPod("ns-2", "pod-2").ObjectMeta(builder.WithLabels("restore-resource")).Result(),
).Done(),
apiResources: []*test.APIResource{test.Pods()},
actions: map[*recordResourcesAction][]string{
new(recordResourcesAction).ForResource("pods").ForLabelSelector("restore-resource"): {"ns-1/pod-1", "ns-2/pod-2"},
},
},
{
name: "multiple actions, each with a different resource selector using short name, run for matching resources",
restore: defaultRestore().Result(),

View File

@@ -1,5 +1,5 @@
/*
Copyright 2017, 2019 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -23,16 +23,14 @@ import (
"github.com/pkg/errors"
corev1api "k8s.io/api/core/v1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
corev1listers "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/clientcmd"
)
// NamespaceAndName returns a string in the format <namespace>/<name>
@@ -148,8 +146,23 @@ func GetVolumeDirectory(pod *corev1api.Pod, volumeName string, pvcLister corev1l
return pvc.Spec.VolumeName, nil
}
// IsCRDReady checks a CRD to see if it's ready, with both the Established and NamesAccepted conditions.
func IsCRDReady(crd *apiextv1beta1.CustomResourceDefinition) bool {
// IsV1CRDReady checks a v1 CRD to see if it's ready, with both the Established and NamesAccepted conditions.
func IsV1CRDReady(crd *apiextv1.CustomResourceDefinition) bool {
var isEstablished, namesAccepted bool
for _, cond := range crd.Status.Conditions {
if cond.Type == apiextv1.Established && cond.Status == apiextv1.ConditionTrue {
isEstablished = true
}
if cond.Type == apiextv1.NamesAccepted && cond.Status == apiextv1.ConditionTrue {
namesAccepted = true
}
}
return (isEstablished && namesAccepted)
}
// IsV1Beta1CRDReady checks a v1beta1 CRD to see if it's ready, with both the Established and NamesAccepted conditions.
func IsV1Beta1CRDReady(crd *apiextv1beta1.CustomResourceDefinition) bool {
var isEstablished, namesAccepted bool
for _, cond := range crd.Status.Conditions {
if cond.Type == apiextv1beta1.Established && cond.Status == apiextv1beta1.ConditionTrue {
@@ -164,8 +177,8 @@ func IsCRDReady(crd *apiextv1beta1.CustomResourceDefinition) bool {
}
// IsUnstructuredCRDReady checks an unstructured CRD to see if it's ready, with both the Established and NamesAccepted conditions.
// TODO: Delete this function and use IsCRDReady when the upstream runtime.FromUnstructured function properly handles int64 field conversions.
// Duplicated function because the velero install package uses IsCRDReady with the beta types.
// TODO: Delete this function and use IsV1CRDReady/IsV1Beta1CRDReady when the upstream runtime.FromUnstructured function properly handles int64 field conversions.
// Duplicated function because the velero install package uses IsV1CRDReady/IsV1Beta1CRDReady with instances of v1/v1beta1 types.
// See https://github.com/kubernetes/kubernetes/issues/87675
// This is different from the fix for https://github.com/vmware-tanzu/velero/issues/2319 because here,
// we need to account for *both* v1beta1 and v1 CRDs, so doing marshalling into JSON to convert to a Go type may not be as useful here, unless we do
@@ -210,6 +223,7 @@ func IsUnstructuredCRDReady(crd *unstructured.Unstructured) (bool, error) {
// Here is the actual logic of the function
// Cast the API's types into strings since we're pulling strings out of the unstructured data.
// We are using the v1beta1 constants here but they are the same as the v1 constants.
if conditionType == string(apiextv1beta1.Established) && status == string(apiextv1beta1.ConditionTrue) {
isEstablished = true
}
@@ -220,26 +234,3 @@ func IsUnstructuredCRDReady(crd *unstructured.Unstructured) (bool, error) {
return (isEstablished && namesAccepted), nil
}
// GetClusterClient instantiates and returns a client for the cluster.
func GetClusterClient() (*kubernetes.Clientset, *apiextensionsclientset.Clientset, error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
configOverrides := &clientcmd.ConfigOverrides{}
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
clientConfig, err := kubeConfig.ClientConfig()
if err != nil {
return nil, nil, errors.WithStack(err)
}
client, err := kubernetes.NewForConfig(clientConfig)
if err != nil {
return nil, nil, errors.WithStack(err)
}
extensionClientSet, err := apiextensionsclientset.NewForConfig(clientConfig)
if err != nil {
return nil, nil, errors.WithStack(err)
}
return client, extensionClientSet, nil
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2017 the Velero contributors.
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -210,7 +211,7 @@ func TestGetVolumeDirectorySuccess(t *testing.T) {
}
}
func TestIsCRDReady(t *testing.T) {
func TestIsV1Beta1CRDReady(t *testing.T) {
tests := []struct {
name string
crd *apiextv1beta1.CustomResourceDefinition
@@ -218,33 +219,72 @@ func TestIsCRDReady(t *testing.T) {
}{
{
name: "CRD is not established & not accepting names - not ready",
crd: builder.ForCustomResourceDefinition("MyCRD").Result(),
crd: builder.ForCustomResourceDefinitionV1Beta1("MyCRD").Result(),
want: false,
},
{
name: "CRD is established & not accepting names - not ready",
crd: builder.ForCustomResourceDefinition("MyCRD").
Condition(builder.ForCustomResourceDefinitionCondition().Type(apiextv1beta1.Established).Status(apiextv1beta1.ConditionTrue).Result()).Result(),
crd: builder.ForCustomResourceDefinitionV1Beta1("MyCRD").
Condition(builder.ForCustomResourceDefinitionV1Beta1Condition().Type(apiextv1beta1.Established).Status(apiextv1beta1.ConditionTrue).Result()).Result(),
want: false,
},
{
name: "CRD is not established & accepting names - not ready",
crd: builder.ForCustomResourceDefinition("MyCRD").
Condition(builder.ForCustomResourceDefinitionCondition().Type(apiextv1beta1.NamesAccepted).Status(apiextv1beta1.ConditionTrue).Result()).Result(),
crd: builder.ForCustomResourceDefinitionV1Beta1("MyCRD").
Condition(builder.ForCustomResourceDefinitionV1Beta1Condition().Type(apiextv1beta1.NamesAccepted).Status(apiextv1beta1.ConditionTrue).Result()).Result(),
want: false,
},
{
name: "CRD is established & accepting names - ready",
crd: builder.ForCustomResourceDefinition("MyCRD").
Condition(builder.ForCustomResourceDefinitionCondition().Type(apiextv1beta1.Established).Status(apiextv1beta1.ConditionTrue).Result()).
Condition(builder.ForCustomResourceDefinitionCondition().Type(apiextv1beta1.NamesAccepted).Status(apiextv1beta1.ConditionTrue).Result()).
crd: builder.ForCustomResourceDefinitionV1Beta1("MyCRD").
Condition(builder.ForCustomResourceDefinitionV1Beta1Condition().Type(apiextv1beta1.Established).Status(apiextv1beta1.ConditionTrue).Result()).
Condition(builder.ForCustomResourceDefinitionV1Beta1Condition().Type(apiextv1beta1.NamesAccepted).Status(apiextv1beta1.ConditionTrue).Result()).
Result(),
want: true,
},
}
for _, tc := range tests {
result := IsCRDReady(tc.crd)
result := IsV1Beta1CRDReady(tc.crd)
assert.Equal(t, tc.want, result)
}
}
func TestIsV1CRDReady(t *testing.T) {
tests := []struct {
name string
crd *apiextv1.CustomResourceDefinition
want bool
}{
{
name: "CRD is not established & not accepting names - not ready",
crd: builder.ForV1CustomResourceDefinition("MyCRD").Result(),
want: false,
},
{
name: "CRD is established & not accepting names - not ready",
crd: builder.ForV1CustomResourceDefinition("MyCRD").
Condition(builder.ForV1CustomResourceDefinitionCondition().Type(apiextv1.Established).Status(apiextv1.ConditionTrue).Result()).Result(),
want: false,
},
{
name: "CRD is not established & accepting names - not ready",
crd: builder.ForV1CustomResourceDefinition("MyCRD").
Condition(builder.ForV1CustomResourceDefinitionCondition().Type(apiextv1.NamesAccepted).Status(apiextv1.ConditionTrue).Result()).Result(),
want: false,
},
{
name: "CRD is established & accepting names - ready",
crd: builder.ForV1CustomResourceDefinition("MyCRD").
Condition(builder.ForV1CustomResourceDefinitionCondition().Type(apiextv1.Established).Status(apiextv1.ConditionTrue).Result()).
Condition(builder.ForV1CustomResourceDefinitionCondition().Type(apiextv1.NamesAccepted).Status(apiextv1.ConditionTrue).Result()).
Result(),
want: true,
},
}
for _, tc := range tests {
result := IsV1CRDReady(tc.crd)
assert.Equal(t, tc.want, result)
}
}
@@ -257,26 +297,26 @@ func TestIsUnstructuredCRDReady(t *testing.T) {
}{
{
name: "CRD is not established & not accepting names - not ready",
crd: builder.ForCustomResourceDefinition("MyCRD").Result(),
crd: builder.ForCustomResourceDefinitionV1Beta1("MyCRD").Result(),
want: false,
},
{
name: "CRD is established & not accepting names - not ready",
crd: builder.ForCustomResourceDefinition("MyCRD").
Condition(builder.ForCustomResourceDefinitionCondition().Type(apiextv1beta1.Established).Status(apiextv1beta1.ConditionTrue).Result()).Result(),
crd: builder.ForCustomResourceDefinitionV1Beta1("MyCRD").
Condition(builder.ForCustomResourceDefinitionV1Beta1Condition().Type(apiextv1beta1.Established).Status(apiextv1beta1.ConditionTrue).Result()).Result(),
want: false,
},
{
name: "CRD is not established & accepting names - not ready",
crd: builder.ForCustomResourceDefinition("MyCRD").
Condition(builder.ForCustomResourceDefinitionCondition().Type(apiextv1beta1.NamesAccepted).Status(apiextv1beta1.ConditionTrue).Result()).Result(),
crd: builder.ForCustomResourceDefinitionV1Beta1("MyCRD").
Condition(builder.ForCustomResourceDefinitionV1Beta1Condition().Type(apiextv1beta1.NamesAccepted).Status(apiextv1beta1.ConditionTrue).Result()).Result(),
want: false,
},
{
name: "CRD is established & accepting names - ready",
crd: builder.ForCustomResourceDefinition("MyCRD").
Condition(builder.ForCustomResourceDefinitionCondition().Type(apiextv1beta1.Established).Status(apiextv1beta1.ConditionTrue).Result()).
Condition(builder.ForCustomResourceDefinitionCondition().Type(apiextv1beta1.NamesAccepted).Status(apiextv1beta1.ConditionTrue).Result()).
crd: builder.ForCustomResourceDefinitionV1Beta1("MyCRD").
Condition(builder.ForCustomResourceDefinitionV1Beta1Condition().Type(apiextv1beta1.Established).Status(apiextv1beta1.ConditionTrue).Result()).
Condition(builder.ForCustomResourceDefinitionV1Beta1Condition().Type(apiextv1beta1.NamesAccepted).Status(apiextv1beta1.ConditionTrue).Result()).
Result(),
want: true,
},

View File

@@ -43,4 +43,14 @@ To run unit tests, use `make test`.
If you need to add or update the vendored dependencies, see [Vendoring dependencies][11].
## Using the main branch
If you are developing or using the main branch, note that you may need to update the Velero CRDs to get new changes as other development work is completed.
```bash
velero install --crds-only --dry-run -o yaml | kubectl apply -f -
```
**NOTE:** You could change the default CRD API version (v1beta1 _or_ v1) if Velero CLI can't discover the Kubernetes preferred CRD API version. The Kubernetes version < 1.16 preferred CRD API version is v1beta1; the Kubernetes version >= 1.16 preferred CRD API version is v1.
[11]: vendoring-dependencies.md

View File

@@ -39,6 +39,8 @@ If you're not yet running at least Velero v1.5, see the following:
velero install --crds-only --dry-run -o yaml | kubectl apply -f -
```
**NOTE:** You could change the default CRD API version (v1beta1 _or_ v1) if Velero CLI can't discover the Kubernetes preferred CRD API version. The Kubernetes version < 1.16 preferred CRD API version is v1beta1; the Kubernetes version >= 1.16 preferred CRD API version is v1.
**NOTE:** If you are upgrading Velero in Kubernetes 1.14.x or earlier, you will need to use `kubectl apply`'s `--validate=false` option when applying the CRD configuration above. See [issue 2077][6] and [issue 2311][7] for more context.
1. Update the container image used by the Velero deployment and, optionally, the restic daemon set:

View File

@@ -459,7 +459,7 @@ cqlsh:demodb> select * from emp;
cqlsh:demodb>
```
It looks like the restore has been successful. Velero v1.1 has successfully restored the Kubenetes objects for the Cassandra application, as well as restored the database and table contents.
It looks like the restore has been successful. Velero v1.1 has successfully restored the Kubernetes objects for the Cassandra application, as well as restored the database and table contents.
## Feedback and Participation

View File

@@ -48,7 +48,8 @@ OUTPUT_DIR := _output/$(GOOS)/$(GOARCH)/bin
GINKGO_FOCUS ?=
VELERO_CLI ?=$$(pwd)/../../_output/bin/$(GOOS)/$(GOARCH)/velero
VELERO_IMAGE ?= velero/velero:main
VELERO_NAMESPACE ?=
CRDS_VERSION ?= v1
VELERO_NAMESPACE ?= velero
CREDS_FILE ?=
BSL_BUCKET ?=
BSL_PREFIX ?=
@@ -79,6 +80,7 @@ run: ginkgo
@$(GINKGO) -v -focus="$(GINKGO_FOCUS)" . -- -velerocli=$(VELERO_CLI) \
-velero-image=$(VELERO_IMAGE) \
-velero-namespace=$(VELERO_NAMESPACE) \
-crds-version=$(CRDS_VERSION) \
-credentials-file=$(CREDS_FILE) \
-bucket=$(BSL_BUCKET) \
-prefix=$(BSL_PREFIX) \

View File

@@ -88,9 +88,9 @@ For example, E2E tests can be run from Velero repository roots using the command
BSL_CONFIG="resourceGroup=$AZURE_BACKUP_RESOURCE_GROUP,storageAccount=$AZURE_STORAGE_ACCOUNT_ID,subscriptionId=$AZURE_BACKUP_SUBSCRIPTION_ID" BSL_BUCKET=<BUCKET_FOR_E2E_TEST_BACKUP> CREDS_FILE=/path/to/azure-creds CLOUD_PROVIDER=azure make test-e2e
```
Please refer to `velero-plugin-for-microsoft-azure` documentation for instruction to [set up permissions for Velero](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure#set-permissions-for-velero) and to [set up azure storage account and blob container](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure#setup-azure-storage-account-and-blob-container)
1. Run Ginko-focused Restore Multi-API Groups tests using an image built for PR #3133 and Minio as the backup storage location:
1. Run Ginko-focused Restore Multi-API Groups tests using Minio as the backup storage location:
```bash
BSL_CONFIG="region=minio,s3ForcePathStyle=\"true\",s3Url=http://192.168.1.124:9000" BSL_PREFIX=veldat BSL_BUCKET=velero CREDS_FILE=~/go/src/github.com/vmware-tanzu/velero/frankie-secrets/credentials-minio PLUGIN_PROVIDER=aws VELERO_IMAGE=projects.registry.vmware.com/tanzu_migrator/velero-pr3133:0.0.5 GINKGO_FOCUS="API group versions" make test-e2e
BSL_CONFIG="region=minio,s3ForcePathStyle=\"true\",s3Url=<ip address>:9000" BSL_PREFIX=<prefix> BSL_BUCKET=<bucket> CREDS_FILE=<absolute path to minio credentials file> CLOUD_PROVIDER=kind OBJECT_STORE_PROVIDER=aws VELERO_NAMESPACE="velero" GINKGO_FOCUS="API group versions" make test-e2e
```
1. Run Velero tests in a kind cluster with AWS (or Minio) as the storage provider and use Microsoft Azure as the storage provider for an additional Backup Storage Location:
```bash
@@ -105,3 +105,11 @@ For example, E2E tests can be run from Velero repository roots using the command
Velero E2E tests uses [Ginkgo](https://onsi.github.io/ginkgo/) testing framework which allows a subset of the tests to be run using the [`-focus` and `-skip`](https://onsi.github.io/ginkgo/#focused-specs) flags to ginkgo.
The `-focus` flag is passed to ginkgo using the `GINKGO_FOCUS` make variable. This can be used to focus on specific tests.
## Adding tests
### API clients
When adding a test, aim to instantiate an API client only once at the beginning of the test. There is a constructor `newTestClient` that facilitates the configuration and instantiation of clients. Also, please use the `kubebuilder` runtime controller client for any new test, as we will phase out usage of `client-go` API clients.
### Tips
Look for the ⛵ emoji printed at the end of each install and uninstall log. There should not be two install/unintall in a row, and there should be tests between an install and an uninstall.

View File

@@ -1,18 +1,28 @@
/*
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 e2e
import (
"context"
"flag"
"fmt"
"time"
"github.com/google/uuid"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/client-go/kubernetes"
"github.com/vmware-tanzu/velero/pkg/util/kube"
)
var (
@@ -34,12 +44,12 @@ func backup_restore_with_restic() {
func backup_restore_test(useVolumeSnapshots bool) {
var (
client *kubernetes.Clientset
extensionsClient *apiextensionsclientset.Clientset
backupName string
restoreName string
backupName, restoreName string
)
client, err := newTestClient()
Expect(err).To(Succeed(), "Failed to instantiate cluster client for backup tests")
BeforeEach(func() {
if useVolumeSnapshots && cloudProvider == "kind" {
Skip("Volume snapshots not supported on kind")
@@ -49,21 +59,16 @@ func backup_restore_test(useVolumeSnapshots bool) {
uuidgen, err = uuid.NewRandom()
Expect(err).To(Succeed())
if installVelero {
Expect(VeleroInstall(context.Background(), veleroImage, veleroNamespace, cloudProvider, objectStoreProvider, useVolumeSnapshots,
cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, vslConfig, "")).To(Succeed())
Expect(veleroInstall(context.Background(), veleroImage, veleroNamespace, cloudProvider, objectStoreProvider, useVolumeSnapshots,
cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, vslConfig, crdsVersion, "")).To(Succeed())
}
client, extensionsClient, err = kube.GetClusterClient()
Expect(err).To(Succeed(), "Failed to instantiate cluster client")
})
AfterEach(func() {
if installVelero {
timeoutCTX, _ := context.WithTimeout(context.Background(), time.Minute)
err := VeleroUninstall(timeoutCTX, client, extensionsClient, veleroNamespace)
err = veleroUninstall(context.Background(), client.kubebuilder, installVelero, veleroNamespace)
Expect(err).To(Succeed())
}
})
When("kibishii is the sample workload", func() {
@@ -72,7 +77,7 @@ func backup_restore_test(useVolumeSnapshots bool) {
restoreName = "restore-" + uuidgen.String()
// Even though we are using Velero's CloudProvider plugin for object storage, the kubernetes cluster is running on
// KinD. So use the kind installation for Kibishii.
Expect(RunKibishiiTests(client, cloudProvider, veleroCLI, veleroNamespace, backupName, restoreName, "", useVolumeSnapshots)).To(Succeed(),
Expect(runKibishiiTests(client, cloudProvider, veleroCLI, veleroNamespace, backupName, restoreName, "", useVolumeSnapshots)).To(Succeed(),
"Failed to successfully backup and restore Kibishii namespace")
})
@@ -89,7 +94,7 @@ func backup_restore_test(useVolumeSnapshots bool) {
Skip("no additional BSL credentials given, not running multiple BackupStorageLocation with unique credentials tests")
}
Expect(VeleroAddPluginsForProvider(context.TODO(), veleroCLI, veleroNamespace, additionalBSLProvider)).To(Succeed())
Expect(veleroAddPluginsForProvider(context.TODO(), veleroCLI, veleroNamespace, additionalBSLProvider)).To(Succeed())
// Create Secret for additional BSL
secretName := fmt.Sprintf("bsl-credentials-%s", uuidgen)
@@ -98,11 +103,11 @@ func backup_restore_test(useVolumeSnapshots bool) {
secretKey: additionalBSLCredentials,
}
Expect(CreateSecretFromFiles(context.TODO(), client, veleroNamespace, secretName, files)).To(Succeed())
Expect(createSecretFromFiles(context.TODO(), client, veleroNamespace, secretName, files)).To(Succeed())
// Create additional BSL using credential
additionalBsl := fmt.Sprintf("bsl-%s", uuidgen)
Expect(VeleroCreateBackupLocation(context.TODO(),
Expect(veleroCreateBackupLocation(context.TODO(),
veleroCLI,
veleroNamespace,
additionalBsl,
@@ -120,7 +125,7 @@ func backup_restore_test(useVolumeSnapshots bool) {
backupName = fmt.Sprintf("backup-%s-%s", bsl, uuidgen)
restoreName = fmt.Sprintf("restore-%s-%s", bsl, uuidgen)
Expect(RunKibishiiTests(client, cloudProvider, veleroCLI, veleroNamespace, backupName, restoreName, bsl, useVolumeSnapshots)).To(Succeed(),
Expect(runKibishiiTests(client, cloudProvider, veleroCLI, veleroNamespace, backupName, restoreName, bsl, useVolumeSnapshots)).To(Succeed(),
"Failed to successfully backup and restore Kibishii namespace using BSL %s", bsl)
}
})

79
test/e2e/client.go Normal file
View File

@@ -0,0 +1,79 @@
/*
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 e2e
import (
"k8s.io/client-go/kubernetes"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/vmware-tanzu/velero/pkg/client"
)
// testClient contains different API clients that are in use throughout
// the e2e tests.
type testClient struct {
kubebuilder kbclient.Client
// clientGo returns a client-go API client.
//
// Deprecated, TODO(2.0): presuming all controllers and resources are converted to the
// controller runtime framework by v2.0, it is the intent to remove all
// client-go API clients. Please use the controller runtime to make API calls for tests.
clientGo kubernetes.Interface
// dynamicFactory returns a client-go API client for retrieving dynamic clients
// for GroupVersionResources and GroupVersionKinds.
//
// Deprecated, TODO(2.0): presuming all controllers and resources are converted to the
// controller runtime framework by v2.0, it is the intent to remove all
// client-go API clients. Please use the controller runtime to make API calls for tests.
dynamicFactory client.DynamicFactory
}
// newTestClient returns a set of ready-to-use API clients.
func newTestClient() (testClient, error) {
config, err := client.LoadConfig()
if err != nil {
return testClient{}, err
}
f := client.NewFactory("e2e", config)
clientGo, err := f.KubeClient()
if err != nil {
return testClient{}, err
}
kb, err := f.KubebuilderClient()
if err != nil {
return testClient{}, err
}
dynamicClient, err := f.DynamicClient()
if err != nil {
return testClient{}, err
}
factory := client.NewDynamicFactory(dynamicClient)
return testClient{
kubebuilder: kb,
clientGo: clientGo,
dynamicFactory: factory,
}, nil
}

View File

@@ -1,3 +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 e2e
import (
@@ -13,30 +29,29 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"github.com/vmware-tanzu/velero/pkg/builder"
)
// EnsureClusterExists returns whether or not a kubernetes cluster exists for tests to be run on.
func EnsureClusterExists(ctx context.Context) error {
// ensureClusterExists returns whether or not a kubernetes cluster exists for tests to be run on.
func ensureClusterExists(ctx context.Context) error {
return exec.CommandContext(ctx, "kubectl", "cluster-info").Run()
}
// CreateNamespace creates a kubernetes namespace
func CreateNamespace(ctx context.Context, client *kubernetes.Clientset, namespace string) error {
// createNamespace creates a kubernetes namespace
func createNamespace(ctx context.Context, client testClient, namespace string) error {
ns := builder.ForNamespace(namespace).Result()
_, err := client.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})
_, err := client.clientGo.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})
if apierrors.IsAlreadyExists(err) {
return nil
}
return err
}
// WaitForNamespaceDeletion Waits for namespace to be deleted.
func WaitForNamespaceDeletion(interval, timeout time.Duration, client *kubernetes.Clientset, ns string) error {
// waitForNamespaceDeletion waits for namespace to be deleted.
func waitForNamespaceDeletion(interval, timeout time.Duration, client testClient, ns string) error {
err := wait.PollImmediate(interval, timeout, func() (bool, error) {
_, err := client.CoreV1().Namespaces().Get(context.TODO(), ns, metav1.GetOptions{})
_, err := client.clientGo.CoreV1().Namespaces().Get(context.TODO(), ns, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
return true, nil
@@ -49,7 +64,7 @@ func WaitForNamespaceDeletion(interval, timeout time.Duration, client *kubernete
return err
}
func CreateSecretFromFiles(ctx context.Context, client *kubernetes.Clientset, namespace string, name string, files map[string]string) error {
func createSecretFromFiles(ctx context.Context, client testClient, namespace string, name string, files map[string]string) error {
data := make(map[string][]byte)
for key, filePath := range files {
@@ -62,19 +77,17 @@ func CreateSecretFromFiles(ctx context.Context, client *kubernetes.Clientset, na
}
secret := builder.ForSecret(namespace, name).Data(data).Result()
_, err := client.CoreV1().Secrets(namespace).Create(ctx, secret, metav1.CreateOptions{})
_, err := client.clientGo.CoreV1().Secrets(namespace).Create(ctx, secret, metav1.CreateOptions{})
return err
}
/*
Waits until all of the pods have gone to PodRunning state
*/
func WaitForPods(ctx context.Context, client *kubernetes.Clientset, namespace string, pods []string) error {
// waitForPods waits until all of the pods have gone to PodRunning state
func waitForPods(ctx context.Context, client testClient, namespace string, pods []string) error {
timeout := 10 * time.Minute
interval := 5 * time.Second
err := wait.PollImmediate(interval, timeout, func() (bool, error) {
for _, podName := range pods {
checkPod, err := client.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{})
checkPod, err := client.clientGo.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{})
if err != nil {
return false, errors.WithMessage(err, fmt.Sprintf("Failed to verify pod %s/%s is %s", namespace, podName, corev1api.PodRunning))
}

View File

@@ -1,3 +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 e2e
import (
@@ -9,9 +25,9 @@ import (
)
var (
veleroCLI, veleroImage, cloudCredentialsFile, bslConfig, bslBucket, bslPrefix, vslConfig, cloudProvider, objectStoreProvider, veleroNamespace string
additionalBSLProvider, additionalBSLBucket, additionalBSLPrefix, additionalBSLConfig, additionalBSLCredentials string
installVelero bool
veleroCLI, veleroImage, cloudCredentialsFile, bslConfig, bslBucket, bslPrefix, vslConfig, cloudProvider, objectStoreProvider, veleroNamespace, crdsVersion string
additionalBSLProvider, additionalBSLBucket, additionalBSLPrefix, additionalBSLConfig, additionalBSLCredentials string
installVelero bool
)
func init() {
@@ -33,6 +49,7 @@ func init() {
flag.StringVar(&additionalBSLPrefix, "additional-bsl-prefix", "", "prefix under which all Velero data should be stored within the bucket for additional backup storage location. Optional.")
flag.StringVar(&additionalBSLConfig, "additional-bsl-config", "", "configuration to use for the additional backup storage location. Format is key1=value1,key2=value2")
flag.StringVar(&additionalBSLCredentials, "additional-bsl-credentials-file", "", "file containing credentials for additional backup storage location provider. Required if testing multiple credentials support.")
flag.StringVar(&crdsVersion, "crds-version", "v1", "CRD apiVersion for velero CRD creation.")
}
func TestE2e(t *testing.T) {

View File

@@ -1,3 +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 e2e
import (
@@ -5,24 +21,16 @@ import (
"encoding/json"
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"time"
"github.com/google/uuid"
"github.com/vmware-tanzu/velero/pkg/util/kube"
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/pkg/errors"
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"github.com/vmware-tanzu/velero/pkg/builder"
veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec"
@@ -30,156 +38,145 @@ import (
var _ = Describe("[APIGroup] Velero tests with various CRD API group versions", func() {
var (
resource, group string
certMgrCRD map[string]string
client *kubernetes.Clientset
extensionsClient *apiextensionsclient.Clientset
err error
ctx = context.Background()
resource, group string
err error
ctx = context.Background()
)
client, err := newTestClient()
Expect(err).To(Succeed(), "Failed to instantiate cluster client for group version tests")
BeforeEach(func() {
resource = "rockbands"
group = "music.example.io"
certMgrCRD = map[string]string{
"url": "testdata/enable_api_group_versions/cert-manager.yaml",
"namespace": "cert-manager",
}
client, extensionsClient, err = kube.GetClusterClient() // Currently we ignore the API extensions client
Expect(err).NotTo(HaveOccurred())
err = InstallCRD(ctx, certMgrCRD["url"], certMgrCRD["namespace"])
Expect(err).NotTo(HaveOccurred())
uuidgen, err = uuid.NewRandom()
Expect(err).NotTo(HaveOccurred())
// TODO: install Velero once for the test suite once feature flag is
// removed and velero installation becomes the same as other e2e tests.
if installVelero {
err = veleroInstall(
context.Background(),
veleroImage,
veleroNamespace,
cloudProvider,
objectStoreProvider,
false,
cloudCredentialsFile,
bslBucket,
bslPrefix,
bslConfig,
vslConfig,
crdsVersion,
"EnableAPIGroupVersions", // TODO: remove when feature flag is removed
)
Expect(err).NotTo(HaveOccurred())
}
})
AfterEach(func() {
cmd := exec.CommandContext(ctx, "kubectl", "delete", "namespace", "music-system")
_, _, _ = veleroexec.RunCommand(cmd)
fmt.Printf("Clean up resource: kubectl delete crd %s.%s\n", resource, group)
cmd := exec.CommandContext(ctx, "kubectl", "delete", "crd", resource+"."+group)
_, stderr, err := veleroexec.RunCommand(cmd)
if strings.Contains(stderr, "NotFound") {
fmt.Printf("Ignore error: %v\n", stderr)
err = nil
}
Expect(err).NotTo(HaveOccurred())
cmd = exec.CommandContext(ctx, "kubectl", "delete", "crd", "rockbands.music.example.io")
_, _, _ = veleroexec.RunCommand(cmd)
if installVelero {
err = veleroUninstall(ctx, client.kubebuilder, installVelero, veleroNamespace)
Expect(err).NotTo(HaveOccurred())
}
_ = DeleteCRD(ctx, certMgrCRD["url"], certMgrCRD["namespace"])
})
Context("When EnableAPIGroupVersions flag is set", func() {
It("Should back up API group version and restore by version priority", func() {
Expect(RunEnableAPIGroupVersionsTests(
Expect(runEnableAPIGroupVersionsTests(
ctx,
client,
resource,
group,
client,
extensionsClient,
)).To(Succeed(), "Failed to successfully backup and restore multiple API Groups")
})
})
})
func RunEnableAPIGroupVersionsTests(ctx context.Context, resource, group string, client *kubernetes.Clientset,
extensionsClient *apiextensionsclient.Clientset) error {
func runEnableAPIGroupVersionsTests(ctx context.Context, client testClient, resource, group string) error {
tests := []struct {
name string
namespaces []string
srcCRD map[string]string
srcCrdYaml string
srcCRs map[string]string
tgtCRD map[string]string
tgtCrdYaml string
tgtVer string
cm *corev1api.ConfigMap
gvs map[string][]string
want map[string]map[string]string
}{
{
name: "Target and source cluster preferred versions match; Preferred version v1 is restored (Priority 1, Case A).",
srcCRD: map[string]string{
"url": "testdata/enable_api_group_versions/case-a-source.yaml",
"namespace": "music-system",
},
name: "Target and source cluster preferred versions match; Preferred version v1 is restored (Priority 1, Case A).",
srcCrdYaml: "testdata/enable_api_group_versions/case-a-source.yaml",
srcCRs: map[string]string{
"v1": "testdata/enable_api_group_versions/music_v1_rockband.yaml",
"v1alpha1": "testdata/enable_api_group_versions/music_v1alpha1_rockband.yaml",
},
tgtCRD: map[string]string{
"url": "testdata/enable_api_group_versions/case-a-target.yaml",
"namespace": "music-system",
},
tgtVer: "v1",
cm: nil,
tgtCrdYaml: "testdata/enable_api_group_versions/case-a-target.yaml",
tgtVer: "v1",
cm: nil,
want: map[string]map[string]string{
"annotations": {
"rockbands.music.example.io/originalVersion": "v1",
},
"specs": {
"leadSinger": "John Lennon",
"genre": "60s rock",
},
},
},
{
name: "Latest common non-preferred supported version v2beta2 is restored (Priority 3, Case D).",
srcCRD: map[string]string{
"url": "testdata/enable_api_group_versions/case-b-source-manually-added-mutations.yaml",
"namespace": "music-system",
},
name: "Latest common non-preferred supported version v2beta2 is restored (Priority 3, Case D).",
srcCrdYaml: "testdata/enable_api_group_versions/case-b-source-manually-added-mutations.yaml",
srcCRs: map[string]string{
"v2beta2": "testdata/enable_api_group_versions/music_v2beta2_rockband.yaml",
"v2beta1": "testdata/enable_api_group_versions/music_v2beta1_rockband.yaml",
"v1": "testdata/enable_api_group_versions/music_v1_rockband.yaml",
},
tgtCRD: map[string]string{
"url": "testdata/enable_api_group_versions/case-d-target-manually-added-mutations.yaml",
"namespace": "music-system",
},
tgtVer: "v2beta2",
cm: nil,
tgtCrdYaml: "testdata/enable_api_group_versions/case-d-target-manually-added-mutations.yaml",
tgtVer: "v2beta2",
cm: nil,
want: map[string]map[string]string{
"annotations": {
"rockbands.music.example.io/originalVersion": "v2beta2",
},
"specs": {
"leadSinger": "John Lennon",
"leadGuitar": "George Harrison",
"drummer": "Ringo Starr",
"genre": "60s rock",
},
},
},
{
name: "No common supported versions means no rockbands custom resource is restored.",
srcCRD: map[string]string{
"url": "testdata/enable_api_group_versions/case-a-source.yaml",
"namespace": "music-system",
},
name: "No common supported versions means no rockbands custom resource is restored.",
srcCrdYaml: "testdata/enable_api_group_versions/case-a-source.yaml",
srcCRs: map[string]string{
"v1": "testdata/enable_api_group_versions/music_v1_rockband.yaml",
"v1alpha1": "testdata/enable_api_group_versions/music_v1alpha1_rockband.yaml",
},
tgtCRD: map[string]string{
"url": "testdata/enable_api_group_versions/case-b-target-manually-added-mutations.yaml",
"namespace": "music-system",
},
tgtVer: "",
cm: nil,
want: nil,
tgtCrdYaml: "testdata/enable_api_group_versions/case-b-target-manually-added-mutations.yaml",
tgtVer: "",
cm: nil,
want: nil,
},
{
name: "User config map overrides Priority 3, Case D and restores v2beta1",
srcCRD: map[string]string{
"url": "testdata/enable_api_group_versions/case-b-source-manually-added-mutations.yaml",
"namespace": "music-system",
},
name: "User config map overrides Priority 3, Case D and restores v2beta1",
srcCrdYaml: "testdata/enable_api_group_versions/case-b-source-manually-added-mutations.yaml",
srcCRs: map[string]string{
"v2beta2": "testdata/enable_api_group_versions/music_v2beta2_rockband.yaml",
"v2beta1": "testdata/enable_api_group_versions/music_v2beta1_rockband.yaml",
"v1": "testdata/enable_api_group_versions/music_v1_rockband.yaml",
},
tgtCRD: map[string]string{
"url": "testdata/enable_api_group_versions/case-d-target-manually-added-mutations.yaml",
"namespace": "music-system",
},
tgtVer: "v2beta1",
tgtCrdYaml: "testdata/enable_api_group_versions/case-d-target-manually-added-mutations.yaml",
tgtVer: "v2beta1",
cm: builder.ForConfigMap(veleroNamespace, "enableapigroupversions").Data(
"restoreResourcesVersionPriority",
`rockbands.music.example.io=v2beta1,v2beta2,v2`,
@@ -189,101 +186,119 @@ func RunEnableAPIGroupVersionsTests(ctx context.Context, resource, group string,
"rockbands.music.example.io/originalVersion": "v2beta1",
},
"specs": {
"leadSinger": "John Lennon",
"leadGuitar": "George Harrison",
"genre": "60s rock",
"genre": "60s rock",
},
},
},
{
name: "Restore successful when CRD doesn't (yet) exist in target",
srcCrdYaml: "testdata/enable_api_group_versions/case-a-source.yaml",
srcCRs: map[string]string{
"v1": "testdata/enable_api_group_versions/music_v1_rockband.yaml",
},
tgtCrdYaml: "",
tgtVer: "v1",
cm: nil,
want: map[string]map[string]string{
"annotations": {
"rockbands.music.example.io/originalVersion": "v1",
},
"specs": {
"genre": "60s rock",
},
},
},
}
for i, tc := range tests {
fmt.Printf("\n====== Test Case %d ======\n", i)
fmt.Printf("\n====== Test Case %d: %s ======\n", i, tc.name)
err := InstallCRD(ctx, tc.srcCRD["url"], tc.srcCRD["namespace"])
err := installCRD(ctx, tc.srcCrdYaml)
if err != nil {
return errors.Wrap(err, "installing music-system CRD for source cluster")
return errors.Wrap(err, "install music-system CRD on source cluster")
}
for version, cr := range tc.srcCRs {
ns := resource + "-src-" + version
if err := CreateNamespace(ctx, client, ns); err != nil {
return errors.Wrapf(err, "creating %s namespace", ns)
if err := createNamespace(ctx, client, ns); err != nil {
return errors.Wrapf(err, "create %s namespace", ns)
}
if err := InstallCR(ctx, cr, ns); err != nil {
return errors.Wrapf(err, "installing %s custom resource on source cluster namespace %s", cr, ns)
if err := installCR(ctx, cr, ns); err != nil {
deleteNamespacesOnErr(ctx, tc.namespaces)
return errors.Wrapf(err, "install %s custom resource on source cluster in namespace %s", cr, ns)
}
tc.namespaces = append(tc.namespaces, ns)
}
// TODO - Velero needs to be installed AFTER CRDs are installed because of https://github.com/vmware-tanzu/velero/issues/3471
// Once that issue is fixed, we should install Velero once for the test suite
if installVelero {
VeleroInstall(context.Background(), veleroImage, veleroNamespace, cloudProvider, objectStoreProvider, false,
cloudCredentialsFile, bslBucket, bslPrefix, bslConfig, vslConfig,
"EnableAPIGroupVersions" /* TODO - remove this when the feature flag is removed */)
fmt.Println("Sleep 20s to wait for Velero to stabilize after install.")
time.Sleep(time.Second * 20)
// Restart Velero pods in order to recognize music-system CRD right away
// instead of waiting for discovery helper to refresh. See
// https://github.com/vmware-tanzu/velero/issues/3471.
if err := restartPods(ctx, veleroNamespace); err != nil {
deleteNamespacesOnErr(ctx, tc.namespaces)
return errors.Wrapf(err, "restart Velero pods")
}
backup := "backup-rockbands-" + uuidgen.String() + "-" + strconv.Itoa(i)
namespacesStr := strings.Join(tc.namespaces, ",")
err = VeleroBackupNamespace(ctx, veleroCLI, veleroNamespace, backup, namespacesStr, "", false)
err = veleroBackupNamespace(ctx, veleroCLI, veleroNamespace, backup, namespacesStr, "", false)
if err != nil {
VeleroBackupLogs(ctx, veleroCLI, veleroNamespace, backup)
return errors.Wrapf(err, "backing up %s namespaces on source cluster", namespacesStr)
veleroBackupLogs(ctx, veleroCLI, veleroNamespace, backup)
deleteNamespacesOnErr(ctx, tc.namespaces)
return errors.Wrapf(err, "back up %s namespaces on source cluster", namespacesStr)
}
// Delete music-system CRD and controllers installed on source cluster.
if err := DeleteCRD(ctx, tc.srcCRD["url"], tc.srcCRD["namespace"]); err != nil {
return errors.Wrapf(err, "deleting music-system CRD from source cluster")
if err := deleteCRD(ctx, tc.srcCrdYaml); err != nil {
deleteNamespacesOnErr(ctx, tc.namespaces)
return errors.Wrapf(err, "delete music-system CRD from source cluster")
}
for _, ns := range tc.namespaces {
if err := client.CoreV1().Namespaces().Delete(ctx, ns, metav1.DeleteOptions{}); err != nil {
return errors.Wrapf(err, "deleting %s namespace from source cluster", ns)
}
if err := WaitNamespaceDelete(ctx, ns); err != nil {
return errors.Wrapf(err, "deleting %s namespace from source cluster", ns)
if err := deleteNamespace(ctx, ns); err != nil {
deleteNamespacesOnErr(ctx, tc.namespaces)
return errors.Wrapf(err, "delete %s namespace from source cluster", ns)
}
}
// Install music-system CRD for target cluster.
if err := InstallCRD(ctx, tc.tgtCRD["url"], tc.tgtCRD["namespace"]); err != nil {
return errors.Wrapf(err, "installing music-system CRD for target cluster")
if tc.tgtCrdYaml != "" {
if err := installCRD(ctx, tc.tgtCrdYaml); err != nil {
deleteNamespacesOnErr(ctx, tc.namespaces)
return errors.Wrapf(err, "install music-system CRD on target cluster")
}
}
// Apply config map if there is one.
if tc.cm != nil {
_, err := client.CoreV1().ConfigMaps(veleroNamespace).Create(ctx, tc.cm, metav1.CreateOptions{})
_, err := client.clientGo.CoreV1().ConfigMaps(veleroNamespace).Create(ctx, tc.cm, metav1.CreateOptions{})
if err != nil {
return errors.Wrap(err, "creating config map with user version priorities")
deleteNamespacesOnErr(ctx, tc.namespaces)
return errors.Wrap(err, "create config map with user version priorities")
}
}
// Reset Velero to recognize music-system CRD.
if err := RestartPods(ctx, veleroNamespace); err != nil {
return errors.Wrapf(err, "restarting Velero pods")
if err := restartPods(ctx, veleroNamespace); err != nil {
deleteNamespacesOnErr(ctx, tc.namespaces)
return errors.Wrapf(err, "restart Velero pods")
}
fmt.Println("Sleep 20s to wait for Velero to stabilize after restart.")
time.Sleep(time.Second * 20)
// Restore rockbands namespace.
// Restore rockbands namespaces.
restore := "restore-rockbands-" + uuidgen.String() + "-" + strconv.Itoa(i)
if tc.want != nil {
if err := VeleroRestore(ctx, veleroCLI, veleroNamespace, restore, backup); err != nil {
VeleroRestoreLogs(ctx, veleroCLI, veleroNamespace, restore)
return errors.Wrapf(err, "restoring %s namespaces on target cluster", namespacesStr)
if err := veleroRestore(ctx, veleroCLI, veleroNamespace, restore, backup); err != nil {
veleroRestoreLogs(ctx, veleroCLI, veleroNamespace, restore)
deleteNamespacesOnErr(ctx, tc.namespaces)
return errors.Wrapf(err, "restore %s namespaces on target cluster", namespacesStr)
}
annoSpec, err := resourceInfo(ctx, group, tc.tgtVer, resource)
if err != nil {
deleteNamespacesOnErr(ctx, tc.namespaces)
return errors.Wrapf(
err,
"get annotation and spec from %s.%s/%s object",
@@ -300,6 +315,7 @@ func RunEnableAPIGroupVersionsTests(ctx context.Context, resource, group string,
annoSpec["annotations"],
tc.want["annotations"],
)
deleteNamespacesOnErr(ctx, tc.namespaces)
return errors.New(msg)
}
@@ -310,180 +326,104 @@ func RunEnableAPIGroupVersionsTests(ctx context.Context, resource, group string,
annoSpec["specs"],
tc.want["specs"],
)
deleteNamespacesOnErr(ctx, tc.namespaces)
return errors.New(msg)
}
} else {
// No custom resource should have been restored. Expect "no resource found"
// error during restore.
err := VeleroRestore(ctx, veleroCLI, veleroNamespace, restore, backup)
err := veleroRestore(ctx, veleroCLI, veleroNamespace, restore, backup)
if err.Error() != "Unexpected restore phase got PartiallyFailed, expecting Completed" {
deleteNamespacesOnErr(ctx, tc.namespaces)
return errors.New("expected error but not none")
}
}
// Delete namespaces created for CRs
// Clean up.
for _, ns := range tc.namespaces {
fmt.Println("Delete namespace", ns)
_ = client.CoreV1().Namespaces().Delete(ctx, ns, metav1.DeleteOptions{})
_ = WaitNamespaceDelete(ctx, ns)
deleteNamespace(ctx, ns)
}
// Delete source cluster music-system CRD
_ = DeleteCRD(
ctx,
tc.srcCRD["url"],
tc.srcCRD["namespace"],
)
// Delete target cluster music-system CRD
_ = DeleteCRD(
ctx,
tc.tgtCRD["url"],
tc.srcCRD["namespace"],
)
// Uninstall Velero
if installVelero {
err = VeleroUninstall(ctx, client, extensionsClient, veleroNamespace)
if err != nil {
return err
}
_ = deleteCRD(ctx, tc.srcCrdYaml)
if tc.tgtCrdYaml != "" {
_ = deleteCRD(ctx, tc.tgtCrdYaml)
}
}
return nil
}
func installVeleroForAPIGroups(ctx context.Context) error {
if err := EnsureClusterExists(ctx); err != nil {
return errors.Wrap(err, "check cluster exists")
}
func installCRD(ctx context.Context, yaml string) error {
fmt.Printf("Install CRD with %s.\n", yaml)
cmd := exec.CommandContext(ctx, "kubectl", "apply", "-f", yaml)
// Pass global variables to option parameters.
options, err := GetProviderVeleroInstallOptions(
cloudProvider,
cloudCredentialsFile,
bslBucket,
bslPrefix,
bslConfig,
vslConfig,
getProviderPlugins(cloudProvider),
"EnableAPIGroupVersions",
)
if err != nil {
return errors.Wrap(err, "get velero install options")
}
options.UseRestic = false
options.Features = "EnableAPIGroupVersions"
options.Image = veleroImage
if err := InstallVeleroServer(options); err != nil {
return errors.Wrap(err, "install velero server")
}
return nil
}
func InstallCRD(ctx context.Context, crdFile, ns string) error {
fmt.Printf("Install CRD %s.\n", crdFile)
cmd := exec.CommandContext(ctx, "kubectl", "apply", "-f", crdFile)
_, stderr, err := veleroexec.RunCommand(cmd)
if err != nil {
return errors.Wrap(err, stderr)
}
fmt.Println("Wait for CRD to be ready.")
if err := WaitForPodContainers(ctx, ns); err != nil {
return err
}
return err
return nil
}
// WaitForPodContainers will get the pods and container status in a namespace.
// If the ratio of the number of containers running to total in a pod is not 1,
// it is not ready. Otherwise, if all container ratios are 1, the pod is running.
func WaitForPodContainers(ctx context.Context, ns string) error {
err := wait.Poll(3*time.Second, 4*time.Minute, func() (bool, error) {
cmd := exec.CommandContext(ctx, "kubectl", "get", "pods", "-n", ns)
stdout, stderr, err := veleroexec.RunCommand(cmd)
if err != nil {
return false, errors.Wrap(err, stderr)
}
re := regexp.MustCompile(`(\d)/(\d)\s+Running`)
// Default allRunning needs to be false for when no match is found.
var allRunning bool
for i, v := range re.FindAllStringSubmatch(stdout, -1) {
if i == 0 {
allRunning = true
}
allRunning = v[1] == v[2] && allRunning
}
return allRunning, nil
})
if err == nil {
fmt.Println("Sleep for 20s for cluster to stabilize.")
time.Sleep(time.Second * 20)
}
return err
}
func DeleteCRD(ctx context.Context, crdFile, ns string) error {
fmt.Println("Delete CRD", crdFile)
cmd := exec.CommandContext(ctx, "kubectl", "delete", "-f", crdFile, "--wait")
func deleteCRD(ctx context.Context, yaml string) error {
fmt.Println("Delete CRD", yaml)
cmd := exec.CommandContext(ctx, "kubectl", "delete", "-f", yaml, "--wait")
_, stderr, err := veleroexec.RunCommand(cmd)
if strings.Contains(stderr, "not found") {
return nil
}
if err != nil {
return errors.Wrap(err, stderr)
}
err = wait.Poll(1*time.Second, 3*time.Minute, func() (bool, error) {
cmd := exec.CommandContext(ctx, "kubectl", "get", "namespace", ns)
stdout, stderr, err := veleroexec.RunCommand(cmd)
if strings.Contains(stderr, "not found") {
return true, nil
}
if err != nil {
return false, errors.Wrap(err, stderr)
}
re := regexp.MustCompile(ns)
return re.MatchString(stdout), nil
})
return err
return nil
}
func RestartPods(ctx context.Context, ns string) error {
func restartPods(ctx context.Context, ns string) error {
fmt.Printf("Restart pods in %s namespace.\n", ns)
cmd := exec.CommandContext(ctx, "kubectl", "delete", "pod", "--all", "-n", ns, "--wait=true")
cmd := exec.CommandContext(ctx, "kubectl", "delete", "pod", "--all", "-n", ns)
_, _, err := veleroexec.RunCommand(cmd)
_, stderr, err := veleroexec.RunCommand(cmd)
if strings.Contains(stderr, "not found") {
return nil
}
if err != nil {
return errors.Wrap(err, stderr)
}
return nil
}
if err == nil {
fmt.Println("Wait for pods to be ready.")
if err := WaitForPodContainers(ctx, ns); err != nil {
return err
}
func deleteNamespace(ctx context.Context, ns string) error {
fmt.Println("Delete namespace", ns)
cmd := exec.CommandContext(ctx, "kubectl", "delete", "ns", ns, "--wait")
_, stderr, err := veleroexec.RunCommand(cmd)
if strings.Contains(stderr, "not found") {
return nil
}
if err != nil {
return errors.Wrap(err, stderr)
}
return err
return nil
}
func InstallCR(ctx context.Context, crFile, ns string) error {
// DeleteNamespacesOnErr cleans up the namespaces created for a test cast after an
// error interrupts a test case.
func deleteNamespacesOnErr(ctx context.Context, namespaces []string) {
if len(namespaces) > 0 {
fmt.Println("An error has occurred. Cleaning up test case namespaces.")
}
for _, ns := range namespaces {
deleteNamespace(ctx, ns)
}
}
func installCR(ctx context.Context, crFile, ns string) error {
retries := 5
var stderr string
var err error
@@ -503,22 +443,6 @@ func InstallCR(ctx context.Context, crFile, ns string) error {
return errors.Wrap(err, stderr)
}
func WaitNamespaceDelete(ctx context.Context, ns string) error {
err := wait.Poll(1*time.Second, 3*time.Minute, func() (bool, error) {
cmd := exec.CommandContext(ctx, "kubectl", "get", "namespace", ns)
stdout, stderr, err := veleroexec.RunCommand(cmd)
if err != nil {
return false, errors.Wrap(err, stderr)
}
re := regexp.MustCompile(ns)
return re.MatchString(stdout), nil
})
return err
}
func resourceInfo(ctx context.Context, g, v, r string) (map[string]map[string]string, error) {
rvg := r + "." + v + "." + g
ns := r + "-src-" + v

View File

@@ -1,3 +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 e2e
import (
@@ -8,7 +24,6 @@ import (
"github.com/pkg/errors"
"golang.org/x/net/context"
"k8s.io/client-go/kubernetes"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -77,15 +92,15 @@ func verifyData(ctx context.Context, namespace string, levels int, filesPerLevel
return nil
}
// RunKibishiiTests runs kibishii tests on the provider.
func RunKibishiiTests(client *kubernetes.Clientset, providerName, veleroCLI, veleroNamespace, backupName, restoreName, backupLocation string,
// runKibishiiTests runs kibishii tests on the provider.
func runKibishiiTests(client testClient, providerName, veleroCLI, veleroNamespace, backupName, restoreName, backupLocation string,
useVolumeSnapshots bool) error {
fiveMinTimeout, _ := context.WithTimeout(context.Background(), 5*time.Minute)
oneHourTimeout, _ := context.WithTimeout(context.Background(), time.Minute*60)
timeout := 10 * time.Minute
interval := 5 * time.Second
if err := CreateNamespace(fiveMinTimeout, client, kibishiiNamespace); err != nil {
if err := createNamespace(fiveMinTimeout, client, kibishiiNamespace); err != nil {
return errors.Wrapf(err, "Failed to create namespace %s to install Kibishii workload", kibishiiNamespace)
}
@@ -104,8 +119,8 @@ func RunKibishiiTests(client *kubernetes.Clientset, providerName, veleroCLI, vel
return errors.Wrap(err, "Failed to generate data")
}
if err := VeleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, kibishiiNamespace, backupLocation, useVolumeSnapshots); err != nil {
VeleroBackupLogs(fiveMinTimeout, veleroCLI, veleroNamespace, backupName)
if err := veleroBackupNamespace(oneHourTimeout, veleroCLI, veleroNamespace, backupName, kibishiiNamespace, backupLocation, useVolumeSnapshots); err != nil {
veleroBackupLogs(fiveMinTimeout, veleroCLI, veleroNamespace, backupName)
return errors.Wrapf(err, "Failed to backup kibishii namespace %s", kibishiiNamespace)
}
@@ -118,17 +133,17 @@ func RunKibishiiTests(client *kubernetes.Clientset, providerName, veleroCLI, vel
}
}
fmt.Printf("Simulating a disaster by removing namespace %s\n", kibishiiNamespace)
if err := client.CoreV1().Namespaces().Delete(oneHourTimeout, kibishiiNamespace, metav1.DeleteOptions{}); err != nil {
if err := client.clientGo.CoreV1().Namespaces().Delete(oneHourTimeout, kibishiiNamespace, metav1.DeleteOptions{}); err != nil {
return errors.Wrap(err, "Failed to simulate a disaster")
}
// wait for ns delete
err := WaitForNamespaceDeletion(interval, timeout, client, kibishiiNamespace)
err := waitForNamespaceDeletion(interval, timeout, client, kibishiiNamespace)
if err != nil {
return errors.Wrapf(err, fmt.Sprintf("Failed to wait for deletion of namespace %s", kibishiiNamespace))
}
if err := VeleroRestore(oneHourTimeout, veleroCLI, veleroNamespace, restoreName, backupName); err != nil {
VeleroRestoreLogs(fiveMinTimeout, veleroCLI, veleroNamespace, restoreName)
if err := veleroRestore(oneHourTimeout, veleroCLI, veleroNamespace, restoreName, backupName); err != nil {
veleroRestoreLogs(fiveMinTimeout, veleroCLI, veleroNamespace, restoreName)
return errors.Wrapf(err, "Restore %s failed from backup %s", restoreName, backupName)
}
@@ -145,17 +160,17 @@ func RunKibishiiTests(client *kubernetes.Clientset, providerName, veleroCLI, vel
return errors.Wrap(err, "Failed to verify data generated by kibishii")
}
if err := client.CoreV1().Namespaces().Delete(oneHourTimeout, kibishiiNamespace, metav1.DeleteOptions{}); err != nil {
return errors.Wrapf(err, "Failed to cleanup %s wrokload namespace", kibishiiNamespace)
if err := client.clientGo.CoreV1().Namespaces().Delete(oneHourTimeout, kibishiiNamespace, metav1.DeleteOptions{}); err != nil {
return errors.Wrapf(err, "Failed to cleanup %s workload namespace", kibishiiNamespace)
}
// wait for ns delete
if err = WaitForNamespaceDeletion(interval, timeout, client, kibishiiNamespace); err != nil {
if err = waitForNamespaceDeletion(interval, timeout, client, kibishiiNamespace); err != nil {
return errors.Wrapf(err, fmt.Sprintf("Failed to wait for deletion of namespace %s", kibishiiNamespace))
}
fmt.Printf("kibishii test completed successfully\n")
return nil
}
func waitForKibishiiPods(ctx context.Context, client *kubernetes.Clientset, kibishiiNamespace string) error {
return WaitForPods(ctx, client, kibishiiNamespace, []string{"jump-pad", "etcd0", "etcd1", "etcd2", "kibishii-deployment-0", "kibishii-deployment-1"})
func waitForKibishiiPods(ctx context.Context, client testClient, kibishiiNamespace string) error {
return waitForPods(ctx, client, kibishiiNamespace, []string{"jump-pad", "etcd0", "etcd1", "etcd2", "kibishii-deployment-0", "kibishii-deployment-1"})
}

View File

@@ -4,18 +4,12 @@ This directory contains Kubernetes manifests that are used for the enable API gr
## Documentation
Read more about cert-manager in the [Jet Stack repo](https://github.com/jetstack/cert-manager/blob/master/README.md).
Read more about the music-system custom resource definitions and rockbands custom resources created for Velero tests at [@brito-rafa's repo](https://github.com/brito-rafa/k8s-webhooks/blob/master/examples-for-projectvelero/README.md).
## Reference
These manifests, listed below, come from two different sources: github.com/jetstack/cert-manager and github.com/brito-rafa/k8s-webhooks:
cert-manager.yaml
- source: https://github.com/jetstack/cert-manager/releases/download/v1.0.3/cert-manager.yaml
- license: https://github.com/jetstack/cert-manager/blob/master/LICENSE
These manifests, listed below, come from github.com/brito-rafa/k8s-webhooks:
case-a-source.yaml

View File

@@ -1,10 +1,3 @@
apiVersion: v1
kind: Namespace
metadata:
labels:
control-plane: controller-manager
name: music-system
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
@@ -13,18 +6,6 @@ metadata:
controller-gen.kubebuilder.io/version: v0.2.5
name: rockbands.music.example.io
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /convert
conversionReviewVersions:
- v1
- v1alpha1
group: music.example.io
names:
kind: RockBand
@@ -33,363 +14,77 @@ spec:
singular: rockband
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: RockBand is the Schema for the rockbands API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: RockBandSpec defines the desired state of RockBand
properties:
genre:
type: string
leadSinger:
type: string
numberComponents:
format: int32
type: integer
type: object
status:
description: RockBandStatus defines the observed state of RockBand
properties:
lastPlayed:
type: string
type: object
type: object
served: true
storage: true
subresources:
status: {}
- name: v1alpha1
schema:
openAPIV3Schema:
description: RockBand is the Schema for the rockbands API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: RockBandSpec defines the desired state of RockBand
properties:
genre:
type: string
numberComponents:
format: int32
type: integer
type: object
status:
description: RockBandStatus defines the observed state of RockBand
properties:
lastPlayed:
type: string
required:
- lastPlayed
type: object
type: object
served: true
storage: false
- name: v1
schema:
openAPIV3Schema:
description: RockBand is the Schema for the rockbands API
properties:
apiVersion:
description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources"
type: string
kind:
description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"
type: string
metadata:
type: object
spec:
description: RockBandSpec defines the desired state of RockBand
properties:
genre:
type: string
leadSinger:
type: string
numberComponents:
format: int32
type: integer
type: object
status:
description: RockBandStatus defines the observed state of RockBand
properties:
lastPlayed:
type: string
type: object
type: object
served: true
storage: true
subresources:
status: {}
- name: v1alpha1
schema:
openAPIV3Schema:
description: RockBand is the Schema for the rockbands API
properties:
apiVersion:
description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources"
type: string
kind:
description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"
type: string
metadata:
type: object
spec:
description: RockBandSpec defines the desired state of RockBand
properties:
genre:
type: string
numberComponents:
format: int32
type: integer
type: object
status:
description: RockBandStatus defines the observed state of RockBand
properties:
lastPlayed:
type: string
required:
- lastPlayed
type: object
type: object
served: true
storage: false
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: music-leader-election-role
namespace: music-system
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- configmaps/status
verbs:
- get
- update
- patch
- apiGroups:
- ""
resources:
- events
verbs:
- create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: music-manager-role
rules:
- apiGroups:
- music.example.io
resources:
- rockbands
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- music.example.io
resources:
- rockbands/status
verbs:
- get
- patch
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: music-proxy-role
rules:
- apiGroups:
- authentication.k8s.io
resources:
- tokenreviews
verbs:
- create
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: music-metrics-reader
rules:
- nonResourceURLs:
- /metrics
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: music-leader-election-rolebinding
namespace: music-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: music-leader-election-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: music-manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: music-manager-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: music-proxy-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: music-proxy-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: v1
kind: Service
metadata:
labels:
control-plane: controller-manager
name: music-controller-manager-metrics-service
namespace: music-system
spec:
ports:
- name: https
port: 8443
targetPort: https
selector:
control-plane: controller-manager
---
apiVersion: v1
kind: Service
metadata:
name: music-webhook-service
namespace: music-system
spec:
ports:
- port: 443
targetPort: 9443
selector:
control-plane: controller-manager
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
control-plane: controller-manager
name: music-controller-manager
namespace: music-system
spec:
replicas: 1
selector:
matchLabels:
control-plane: controller-manager
template:
metadata:
labels:
control-plane: controller-manager
spec:
containers:
- args:
- --secure-listen-address=0.0.0.0:8443
- --upstream=http://127.0.0.1:8080/
- --logtostderr=true
- --v=10
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0
name: kube-rbac-proxy
ports:
- containerPort: 8443
name: https
- args:
- --metrics-addr=127.0.0.1:8080
- --enable-leader-election
command:
- /manager
image: quay.io/brito_rafa/music-controller:case-a-source-v0.1
name: manager
ports:
- containerPort: 9443
name: webhook-server
protocol: TCP
resources:
limits:
cpu: 100m
memory: 30Mi
requests:
cpu: 100m
memory: 20Mi
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
terminationGracePeriodSeconds: 10
volumes:
- name: cert
secret:
defaultMode: 420
secretName: webhook-server-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: music-serving-cert
namespace: music-system
spec:
dnsNames:
- music-webhook-service.music-system.svc
- music-webhook-service.music-system.svc.cluster.local
issuerRef:
kind: Issuer
name: music-selfsigned-issuer
secretName: webhook-server-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: music-selfsigned-issuer
namespace: music-system
spec:
selfSigned: {}
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: music-system/music-serving-cert
name: music-mutating-webhook-configuration
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /mutate-music-example-io-v1-rockband
failurePolicy: Fail
name: mrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- rockbands
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: music-system/music-serving-cert
name: music-validating-webhook-configuration
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /validate-music-example-io-v1-rockband
failurePolicy: Fail
name: vrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- rockbands

View File

@@ -1,10 +1,3 @@
apiVersion: v1
kind: Namespace
metadata:
labels:
control-plane: controller-manager
name: music-system
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
@@ -13,18 +6,6 @@ metadata:
controller-gen.kubebuilder.io/version: v0.2.5
name: rockbands.music.example.io
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /convert
conversionReviewVersions:
- v1
- v1alpha1
group: music.example.io
names:
kind: RockBand
@@ -33,367 +14,81 @@ spec:
singular: rockband
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: RockBand is the Schema for the rockbands API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: RockBandSpec defines the desired state of RockBand
properties:
genre:
type: string
leadSinger:
type: string
numberComponents:
format: int32
type: integer
type: object
status:
description: RockBandStatus defines the observed state of RockBand
properties:
lastPlayed:
type: string
type: object
type: object
served: true
storage: true
subresources:
status: {}
- name: v2beta1
schema:
openAPIV3Schema:
description: RockBand is the Schema for the rockbands API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: RockBandSpec defines the desired state of RockBand
properties:
genre:
type: string
leadGuitar:
type: string
leadSinger:
type: string
numberComponents:
format: int32
type: integer
type: object
status:
description: RockBandStatus defines the observed state of RockBand
properties:
lastPlayed:
type: string
required:
- lastPlayed
type: object
type: object
served: true
storage: false
- name: v1
schema:
openAPIV3Schema:
description: RockBand is the Schema for the rockbands API
properties:
apiVersion:
description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources"
type: string
kind:
description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"
type: string
metadata:
type: object
spec:
description: RockBandSpec defines the desired state of RockBand
properties:
genre:
type: string
leadSinger:
type: string
numberComponents:
format: int32
type: integer
type: object
status:
description: RockBandStatus defines the observed state of RockBand
properties:
lastPlayed:
type: string
type: object
type: object
served: true
storage: true
subresources:
status: {}
- name: v2beta1
schema:
openAPIV3Schema:
description: RockBand is the Schema for the rockbands API
properties:
apiVersion:
description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources"
type: string
kind:
description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"
type: string
metadata:
type: object
spec:
description: RockBandSpec defines the desired state of RockBand
properties:
genre:
type: string
leadGuitar:
type: string
leadSinger:
type: string
numberComponents:
format: int32
type: integer
type: object
status:
description: RockBandStatus defines the observed state of RockBand
properties:
lastPlayed:
type: string
required:
- lastPlayed
type: object
type: object
served: true
storage: false
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: music-leader-election-role
namespace: music-system
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- configmaps/status
verbs:
- get
- update
- patch
- apiGroups:
- ""
resources:
- events
verbs:
- create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: music-manager-role
rules:
- apiGroups:
- music.example.io
resources:
- rockbands
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- music.example.io
resources:
- rockbands/status
verbs:
- get
- patch
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: music-proxy-role
rules:
- apiGroups:
- authentication.k8s.io
resources:
- tokenreviews
verbs:
- create
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: music-metrics-reader
rules:
- nonResourceURLs:
- /metrics
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: music-leader-election-rolebinding
namespace: music-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: music-leader-election-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: music-manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: music-manager-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: music-proxy-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: music-proxy-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: v1
kind: Service
metadata:
labels:
control-plane: controller-manager
name: music-controller-manager-metrics-service
namespace: music-system
spec:
ports:
- name: https
port: 8443
targetPort: https
selector:
control-plane: controller-manager
---
apiVersion: v1
kind: Service
metadata:
name: music-webhook-service
namespace: music-system
spec:
ports:
- port: 443
targetPort: 9443
selector:
control-plane: controller-manager
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
control-plane: controller-manager
name: music-controller-manager
namespace: music-system
spec:
replicas: 1
selector:
matchLabels:
control-plane: controller-manager
template:
metadata:
labels:
control-plane: controller-manager
spec:
containers:
- args:
- --secure-listen-address=0.0.0.0:8443
- --upstream=http://127.0.0.1:8080/
- --logtostderr=true
- --v=10
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0
name: kube-rbac-proxy
ports:
- containerPort: 8443
name: https
- args:
- --metrics-addr=127.0.0.1:8080
- --enable-leader-election
command:
- /manager
image: quay.io/brito_rafa/music-controller:case-a-target-v0.2
name: manager
ports:
- containerPort: 9443
name: webhook-server
protocol: TCP
resources:
limits:
cpu: 100m
memory: 30Mi
requests:
cpu: 100m
memory: 20Mi
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
terminationGracePeriodSeconds: 10
volumes:
- name: cert
secret:
defaultMode: 420
secretName: webhook-server-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: music-serving-cert
namespace: music-system
spec:
dnsNames:
- music-webhook-service.music-system.svc
- music-webhook-service.music-system.svc.cluster.local
issuerRef:
kind: Issuer
name: music-selfsigned-issuer
secretName: webhook-server-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: music-selfsigned-issuer
namespace: music-system
spec:
selfSigned: {}
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: music-system/music-serving-cert
name: music-mutating-webhook-configuration
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /mutate-music-example-io-v2beta1-rockband
failurePolicy: Fail
name: mrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v2beta1
operations:
- CREATE
- UPDATE
resources:
- rockbands
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: music-system/music-serving-cert
name: music-validating-webhook-configuration
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /validate-music-example-io-v1-rockband
failurePolicy: Fail
name: vrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- rockbands

View File

@@ -1,10 +1,3 @@
apiVersion: v1
kind: Namespace
metadata:
labels:
control-plane: controller-manager
name: music-system
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
@@ -13,19 +6,6 @@ metadata:
controller-gen.kubebuilder.io/version: v0.2.5
name: rockbands.music.example.io
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /convert
conversionReviewVersions:
- v1
- v2beta1
- v2beta2
group: music.example.io
names:
kind: RockBand
@@ -151,325 +131,3 @@ status:
plural: ""
conditions: []
storedVersions: []
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: music-leader-election-role
namespace: music-system
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- configmaps/status
verbs:
- get
- update
- patch
- apiGroups:
- ""
resources:
- events
verbs:
- create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: music-manager-role
rules:
- apiGroups:
- music.example.io
resources:
- rockbands
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- music.example.io
resources:
- rockbands/status
verbs:
- get
- patch
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: music-proxy-role
rules:
- apiGroups:
- authentication.k8s.io
resources:
- tokenreviews
verbs:
- create
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: music-metrics-reader
rules:
- nonResourceURLs:
- /metrics
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: music-leader-election-rolebinding
namespace: music-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: music-leader-election-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: music-manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: music-manager-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: music-proxy-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: music-proxy-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: v1
kind: Service
metadata:
labels:
control-plane: controller-manager
name: music-controller-manager-metrics-service
namespace: music-system
spec:
ports:
- name: https
port: 8443
targetPort: https
selector:
control-plane: controller-manager
---
apiVersion: v1
kind: Service
metadata:
name: music-webhook-service
namespace: music-system
spec:
ports:
- port: 443
targetPort: 9443
selector:
control-plane: controller-manager
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
control-plane: controller-manager
name: music-controller-manager
namespace: music-system
spec:
replicas: 1
selector:
matchLabels:
control-plane: controller-manager
template:
metadata:
labels:
control-plane: controller-manager
spec:
containers:
- args:
- --secure-listen-address=0.0.0.0:8443
- --upstream=http://127.0.0.1:8080/
- --logtostderr=true
- --v=10
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0
name: kube-rbac-proxy
ports:
- containerPort: 8443
name: https
- args:
- --metrics-addr=127.0.0.1:8080
- --enable-leader-election
command:
- /manager
image: quay.io/brito_rafa/music-controller:case-b-source-v0.1
name: manager
ports:
- containerPort: 9443
name: webhook-server
protocol: TCP
resources:
limits:
cpu: 100m
memory: 30Mi
requests:
cpu: 100m
memory: 20Mi
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
terminationGracePeriodSeconds: 10
volumes:
- name: cert
secret:
defaultMode: 420
secretName: webhook-server-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: music-serving-cert
namespace: music-system
spec:
dnsNames:
- music-webhook-service.music-system.svc
- music-webhook-service.music-system.svc.cluster.local
issuerRef:
kind: Issuer
name: music-selfsigned-issuer
secretName: webhook-server-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: music-selfsigned-issuer
namespace: music-system
spec:
selfSigned: {}
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: music-system/music-serving-cert
name: music-mutating-webhook-configuration
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /mutate-music-example-io-v2beta2-rockband
failurePolicy: Fail
name: mrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v2beta2
operations:
- CREATE
- UPDATE
resources:
- rockbands
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /mutate-music-example-io-v2beta1-rockband
failurePolicy: Fail
name: mrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v2beta1
operations:
- CREATE
- UPDATE
resources:
- rockbands
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /mutate-music-example-io-v1-rockband
failurePolicy: Fail
name: mrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- rockbands
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: music-system/music-serving-cert
name: music-validating-webhook-configuration
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /validate-music-example-io-v2beta2-rockband
failurePolicy: Fail
name: vrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v2beta2
operations:
- CREATE
- UPDATE
resources:
- rockbands

View File

@@ -1,10 +1,3 @@
apiVersion: v1
kind: Namespace
metadata:
labels:
control-plane: controller-manager
name: music-system
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
@@ -13,19 +6,6 @@ metadata:
controller-gen.kubebuilder.io/version: v0.2.5
name: rockbands.music.example.io
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /convert
conversionReviewVersions:
- v2beta2
- v2beta1
- v1
group: music.example.io
names:
kind: RockBand
@@ -116,307 +96,3 @@ status:
plural: ""
conditions: []
storedVersions: []
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: music-leader-election-role
namespace: music-system
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- configmaps/status
verbs:
- get
- update
- patch
- apiGroups:
- ""
resources:
- events
verbs:
- create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: music-manager-role
rules:
- apiGroups:
- music.example.io
resources:
- rockbands
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- music.example.io
resources:
- rockbands/status
verbs:
- get
- patch
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: music-proxy-role
rules:
- apiGroups:
- authentication.k8s.io
resources:
- tokenreviews
verbs:
- create
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: music-metrics-reader
rules:
- nonResourceURLs:
- /metrics
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: music-leader-election-rolebinding
namespace: music-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: music-leader-election-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: music-manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: music-manager-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: music-proxy-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: music-proxy-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: v1
kind: Service
metadata:
labels:
control-plane: controller-manager
name: music-controller-manager-metrics-service
namespace: music-system
spec:
ports:
- name: https
port: 8443
targetPort: https
selector:
control-plane: controller-manager
---
apiVersion: v1
kind: Service
metadata:
name: music-webhook-service
namespace: music-system
spec:
ports:
- port: 443
targetPort: 9443
selector:
control-plane: controller-manager
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
control-plane: controller-manager
name: music-controller-manager
namespace: music-system
spec:
replicas: 1
selector:
matchLabels:
control-plane: controller-manager
template:
metadata:
labels:
control-plane: controller-manager
spec:
containers:
- args:
- --secure-listen-address=0.0.0.0:8443
- --upstream=http://127.0.0.1:8080/
- --logtostderr=true
- --v=10
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0
name: kube-rbac-proxy
ports:
- containerPort: 8443
name: https
- args:
- --metrics-addr=127.0.0.1:8080
- --enable-leader-election
command:
- /manager
image: quay.io/brito_rafa/music-controller:case-b-target-v0.1
name: manager
ports:
- containerPort: 9443
name: webhook-server
protocol: TCP
resources:
limits:
cpu: 100m
memory: 30Mi
requests:
cpu: 100m
memory: 20Mi
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
terminationGracePeriodSeconds: 10
volumes:
- name: cert
secret:
defaultMode: 420
secretName: webhook-server-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: music-serving-cert
namespace: music-system
spec:
dnsNames:
- music-webhook-service.music-system.svc
- music-webhook-service.music-system.svc.cluster.local
issuerRef:
kind: Issuer
name: music-selfsigned-issuer
secretName: webhook-server-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: music-selfsigned-issuer
namespace: music-system
spec:
selfSigned: {}
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: music-system/music-serving-cert
name: music-mutating-webhook-configuration
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /mutate-music-example-io-v2beta1-rockband
failurePolicy: Fail
name: mrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v2beta1
operations:
- CREATE
- UPDATE
resources:
- rockbands
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /mutate-music-example-io-v2beta2-rockband
failurePolicy: Fail
name: mrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v2beta2
operations:
- CREATE
- UPDATE
resources:
- rockbands
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: music-system/music-serving-cert
name: music-validating-webhook-configuration
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /validate-music-example-io-v2beta2-rockband
failurePolicy: Fail
name: vrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v2beta2
operations:
- CREATE
- UPDATE
resources:
- rockbands

View File

@@ -1,10 +1,3 @@
apiVersion: v1
kind: Namespace
metadata:
labels:
control-plane: controller-manager
name: music-system
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
@@ -13,18 +6,6 @@ metadata:
controller-gen.kubebuilder.io/version: v0.2.5
name: rockbands.music.example.io
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /convert
conversionReviewVersions:
- v2
- v1
group: music.example.io
names:
kind: RockBand
@@ -115,307 +96,3 @@ status:
plural: ""
conditions: []
storedVersions: []
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: music-leader-election-role
namespace: music-system
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- configmaps/status
verbs:
- get
- update
- patch
- apiGroups:
- ""
resources:
- events
verbs:
- create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: music-manager-role
rules:
- apiGroups:
- music.example.io
resources:
- rockbands
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- music.example.io
resources:
- rockbands/status
verbs:
- get
- patch
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: music-proxy-role
rules:
- apiGroups:
- authentication.k8s.io
resources:
- tokenreviews
verbs:
- create
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: music-metrics-reader
rules:
- nonResourceURLs:
- /metrics
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: music-leader-election-rolebinding
namespace: music-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: music-leader-election-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: music-manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: music-manager-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: music-proxy-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: music-proxy-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: v1
kind: Service
metadata:
labels:
control-plane: controller-manager
name: music-controller-manager-metrics-service
namespace: music-system
spec:
ports:
- name: https
port: 8443
targetPort: https
selector:
control-plane: controller-manager
---
apiVersion: v1
kind: Service
metadata:
name: music-webhook-service
namespace: music-system
spec:
ports:
- port: 443
targetPort: 9443
selector:
control-plane: controller-manager
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
control-plane: controller-manager
name: music-controller-manager
namespace: music-system
spec:
replicas: 1
selector:
matchLabels:
control-plane: controller-manager
template:
metadata:
labels:
control-plane: controller-manager
spec:
containers:
- args:
- --secure-listen-address=0.0.0.0:8443
- --upstream=http://127.0.0.1:8080/
- --logtostderr=true
- --v=10
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0
name: kube-rbac-proxy
ports:
- containerPort: 8443
name: https
- args:
- --metrics-addr=127.0.0.1:8080
- --enable-leader-election
command:
- /manager
image: quay.io/brito_rafa/music-controller:case-c-target-v0.1
name: manager
ports:
- containerPort: 9443
name: webhook-server
protocol: TCP
resources:
limits:
cpu: 100m
memory: 30Mi
requests:
cpu: 100m
memory: 20Mi
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
terminationGracePeriodSeconds: 10
volumes:
- name: cert
secret:
defaultMode: 420
secretName: webhook-server-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: music-serving-cert
namespace: music-system
spec:
dnsNames:
- music-webhook-service.music-system.svc
- music-webhook-service.music-system.svc.cluster.local
issuerRef:
kind: Issuer
name: music-selfsigned-issuer
secretName: webhook-server-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: music-selfsigned-issuer
namespace: music-system
spec:
selfSigned: {}
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: music-system/music-serving-cert
name: music-mutating-webhook-configuration
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /mutate-music-example-io-v1-rockband
failurePolicy: Fail
name: mrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- rockbands
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /mutate-music-example-io-v2-rockband
failurePolicy: Fail
name: mrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v2
operations:
- CREATE
- UPDATE
resources:
- rockbands
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: music-system/music-serving-cert
name: music-validating-webhook-configuration
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /validate-music-example-io-v1-rockband
failurePolicy: Fail
name: vrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- rockbands

View File

@@ -1,10 +1,3 @@
apiVersion: v1
kind: Namespace
metadata:
labels:
control-plane: controller-manager
name: music-system
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
@@ -13,20 +6,6 @@ metadata:
controller-gen.kubebuilder.io/version: v0.2.5
name: rockbands.music.example.io
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /convert
conversionReviewVersions:
- v2
- v2beta2
- v2beta1
- v1
group: music.example.io
names:
kind: RockBand
@@ -158,325 +137,3 @@ status:
plural: ""
conditions: []
storedVersions: []
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: music-leader-election-role
namespace: music-system
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- configmaps/status
verbs:
- get
- update
- patch
- apiGroups:
- ""
resources:
- events
verbs:
- create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: music-manager-role
rules:
- apiGroups:
- music.example.io
resources:
- rockbands
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- music.example.io
resources:
- rockbands/status
verbs:
- get
- patch
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: music-proxy-role
rules:
- apiGroups:
- authentication.k8s.io
resources:
- tokenreviews
verbs:
- create
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: music-metrics-reader
rules:
- nonResourceURLs:
- /metrics
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: music-leader-election-rolebinding
namespace: music-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: music-leader-election-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: music-manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: music-manager-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: music-proxy-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: music-proxy-role
subjects:
- kind: ServiceAccount
name: default
namespace: music-system
---
apiVersion: v1
kind: Service
metadata:
labels:
control-plane: controller-manager
name: music-controller-manager-metrics-service
namespace: music-system
spec:
ports:
- name: https
port: 8443
targetPort: https
selector:
control-plane: controller-manager
---
apiVersion: v1
kind: Service
metadata:
name: music-webhook-service
namespace: music-system
spec:
ports:
- port: 443
targetPort: 9443
selector:
control-plane: controller-manager
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
control-plane: controller-manager
name: music-controller-manager
namespace: music-system
spec:
replicas: 1
selector:
matchLabels:
control-plane: controller-manager
template:
metadata:
labels:
control-plane: controller-manager
spec:
containers:
- args:
- --secure-listen-address=0.0.0.0:8443
- --upstream=http://127.0.0.1:8080/
- --logtostderr=true
- --v=10
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0
name: kube-rbac-proxy
ports:
- containerPort: 8443
name: https
- args:
- --metrics-addr=127.0.0.1:8080
- --enable-leader-election
command:
- /manager
image: quay.io/brito_rafa/music-controller:case-d-target-v0.1
name: manager
ports:
- containerPort: 9443
name: webhook-server
protocol: TCP
resources:
limits:
cpu: 100m
memory: 30Mi
requests:
cpu: 100m
memory: 20Mi
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
terminationGracePeriodSeconds: 10
volumes:
- name: cert
secret:
defaultMode: 420
secretName: webhook-server-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: music-serving-cert
namespace: music-system
spec:
dnsNames:
- music-webhook-service.music-system.svc
- music-webhook-service.music-system.svc.cluster.local
issuerRef:
kind: Issuer
name: music-selfsigned-issuer
secretName: webhook-server-cert
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
name: music-selfsigned-issuer
namespace: music-system
spec:
selfSigned: {}
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: music-system/music-serving-cert
name: music-mutating-webhook-configuration
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /mutate-music-example-io-v2beta2-rockband
failurePolicy: Fail
name: mrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v2beta2
operations:
- CREATE
- UPDATE
resources:
- rockbands
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /mutate-music-example-io-v2beta1-rockband
failurePolicy: Fail
name: mrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v2beta1
operations:
- CREATE
- UPDATE
resources:
- rockbands
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /mutate-music-example-io-v2-rockband
failurePolicy: Fail
name: mrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v2
operations:
- CREATE
- UPDATE
resources:
- rockbands
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
annotations:
cert-manager.io/inject-ca-from: music-system/music-serving-cert
name: music-validating-webhook-configuration
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: music-webhook-service
namespace: music-system
path: /validate-music-example-io-v2beta2-rockband
failurePolicy: Fail
name: vrockband.kb.io
rules:
- apiGroups:
- music.example.io
apiVersions:
- v2beta2
operations:
- CREATE
- UPDATE
resources:
- rockbands

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,5 @@ metadata:
annotations:
rockbands.music.example.io/originalVersion: v1
spec:
# Add fields here
genre: '60s rock'
genre: "60s rock"
numberComponents: 4
leadSinger: John

View File

@@ -5,7 +5,5 @@ metadata:
annotations:
rockbands.music.example.io/originalVersion: v1alpha1
spec:
# Add fields here
genre: '60s rock'
genre: "60s rock"
numberComponents: 4

View File

@@ -3,10 +3,5 @@ kind: RockBand
metadata:
name: beatles
spec:
# Add fields here
genre: '60s rock'
genre: "60s rock"
numberComponents: 4
leadSinger: John
leadGuitar: George
drummer: Ringo
bass: Paul

View File

@@ -5,9 +5,5 @@ metadata:
annotations:
rockbands.music.example.io/originalVersion: v2beta1
spec:
# Add fields here
genre: '60s rock'
genre: "60s rock"
numberComponents: 4
leadSinger: John
leadGuitar: George

View File

@@ -5,9 +5,5 @@ metadata:
annotations:
rockbands.music.example.io/originalVersion: v2beta2
spec:
# Add fields here
genre: '60s rock'
genre: "60s rock"
numberComponents: 4
leadSinger: John
leadGuitar: George
drummer: Ringo

View File

@@ -1,3 +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 e2e
import (
@@ -12,21 +28,16 @@ import (
"strings"
"time"
"k8s.io/apimachinery/pkg/util/wait"
veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec"
"github.com/pkg/errors"
"k8s.io/client-go/kubernetes"
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apimachinery/pkg/util/wait"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/client"
cliinstall "github.com/vmware-tanzu/velero/pkg/cmd/cli/install"
"github.com/vmware-tanzu/velero/pkg/cmd/cli/uninstall"
"github.com/vmware-tanzu/velero/pkg/cmd/util/flag"
"github.com/vmware-tanzu/velero/pkg/install"
veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec"
)
func getProviderPlugins(providerName string) []string {
@@ -43,8 +54,8 @@ func getProviderPlugins(providerName string) []string {
}
}
// GetProviderVeleroInstallOptions returns Velero InstallOptions for the provider.
func GetProviderVeleroInstallOptions(
// getProviderVeleroInstallOptions returns Velero InstallOptions for the provider.
func getProviderVeleroInstallOptions(
pluginProvider,
credentialsFile,
objectStoreBucket,
@@ -84,52 +95,44 @@ func GetProviderVeleroInstallOptions(
return io, nil
}
// InstallVeleroServer installs velero in the cluster.
func InstallVeleroServer(io *cliinstall.InstallOptions) error {
config, err := client.LoadConfig()
if err != nil {
return err
}
// installVeleroServer installs velero in the cluster.
func installVeleroServer(io *cliinstall.InstallOptions) error {
vo, err := io.AsVeleroOptions()
if err != nil {
return errors.Wrap(err, "Failed to translate InstallOptions to VeleroOptions for Velero")
}
f := client.NewFactory("e2e", config)
resources, err := install.AllResources(vo)
client, err := newTestClient()
if err != nil {
return errors.Wrap(err, "Failed to install Velero in the cluster")
return errors.Wrap(err, "Failed to instantiate cluster client for installing Velero")
}
dynamicClient, err := f.DynamicClient()
if err != nil {
return err
}
factory := client.NewDynamicFactory(dynamicClient)
errorMsg := "\n\nError installing Velero. Use `kubectl logs deploy/velero -n velero` to check the deploy logs"
err = install.Install(factory, resources, os.Stdout)
resources := install.AllResources(vo)
err = install.Install(client.dynamicFactory, client.kubebuilder, resources, os.Stdout)
if err != nil {
return errors.Wrap(err, errorMsg)
}
fmt.Println("Waiting for Velero deployment to be ready.")
if _, err = install.DeploymentIsReady(factory, io.Namespace); err != nil {
if _, err = install.DeploymentIsReady(client.dynamicFactory, io.Namespace); err != nil {
return errors.Wrap(err, errorMsg)
}
if io.UseRestic {
fmt.Println("Waiting for Velero restic daemonset to be ready.")
if _, err = install.DaemonSetIsReady(factory, io.Namespace); err != nil {
if _, err = install.DaemonSetIsReady(client.dynamicFactory, io.Namespace); err != nil {
return errors.Wrap(err, errorMsg)
}
}
fmt.Printf("Velero is installed and ready to be tested in the %s namespace! ⛵ \n", io.Namespace)
return nil
}
// CheckBackupPhase uses veleroCLI to inspect the phase of a Velero backup.
func CheckBackupPhase(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string,
// checkBackupPhase uses veleroCLI to inspect the phase of a Velero backup.
func checkBackupPhase(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string,
expectedPhase velerov1api.BackupPhase) error {
checkCMD := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "backup", "get", "-o", "json",
backupName)
@@ -172,8 +175,8 @@ func CheckBackupPhase(ctx context.Context, veleroCLI string, veleroNamespace str
return nil
}
// CheckRestorePhase uses veleroCLI to inspect the phase of a Velero restore.
func CheckRestorePhase(ctx context.Context, veleroCLI string, veleroNamespace string, restoreName string,
// checkRestorePhase uses veleroCLI to inspect the phase of a Velero restore.
func checkRestorePhase(ctx context.Context, veleroCLI string, veleroNamespace string, restoreName string,
expectedPhase velerov1api.RestorePhase) error {
checkCMD := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "restore", "get", "-o", "json",
restoreName)
@@ -216,8 +219,8 @@ func CheckRestorePhase(ctx context.Context, veleroCLI string, veleroNamespace st
return nil
}
// VeleroBackupNamespace uses the veleroCLI to backup a namespace.
func VeleroBackupNamespace(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string, namespace string, backupLocation string,
// veleroBackupNamespace uses the veleroCLI to backup a namespace.
func veleroBackupNamespace(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string, namespace string, backupLocation string,
useVolumeSnapshots bool) error {
args := []string{
"--namespace", veleroNamespace,
@@ -243,13 +246,13 @@ func VeleroBackupNamespace(ctx context.Context, veleroCLI string, veleroNamespac
if err != nil {
return err
}
err = CheckBackupPhase(ctx, veleroCLI, veleroNamespace, backupName, velerov1api.BackupPhaseCompleted)
err = checkBackupPhase(ctx, veleroCLI, veleroNamespace, backupName, velerov1api.BackupPhaseCompleted)
return err
}
// VeleroRestore uses the veleroCLI to restore from a Velero backup.
func VeleroRestore(ctx context.Context, veleroCLI string, veleroNamespace string, restoreName string, backupName string) error {
// veleroRestore uses the veleroCLI to restore from a Velero backup.
func veleroRestore(ctx context.Context, veleroCLI string, veleroNamespace string, restoreName string, backupName string) error {
restoreCmd := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "create", "restore", restoreName,
"--from-backup", backupName, "--wait")
@@ -260,12 +263,12 @@ func VeleroRestore(ctx context.Context, veleroCLI string, veleroNamespace string
if err != nil {
return err
}
return CheckRestorePhase(ctx, veleroCLI, veleroNamespace, restoreName, velerov1api.RestorePhaseCompleted)
return checkRestorePhase(ctx, veleroCLI, veleroNamespace, restoreName, velerov1api.RestorePhaseCompleted)
}
func VeleroInstall(ctx context.Context, veleroImage string, veleroNamespace string, cloudProvider string, objectStoreProvider string, useVolumeSnapshots bool,
func veleroInstall(ctx context.Context, veleroImage string, veleroNamespace string, cloudProvider string, objectStoreProvider string, useVolumeSnapshots bool,
cloudCredentialsFile string, bslBucket string, bslPrefix string, bslConfig string, vslConfig string,
features string) error {
crdsVersion string, features string) error {
if cloudProvider != "kind" {
if objectStoreProvider != "" {
@@ -278,6 +281,7 @@ func VeleroInstall(ctx context.Context, veleroImage string, veleroNamespace stri
}
}
// Fetch the plugins for the provider before checking for the object store provider below.
providerPlugins := getProviderPlugins(objectStoreProvider)
// TODO - handle this better
@@ -287,12 +291,16 @@ func VeleroInstall(ctx context.Context, veleroImage string, veleroNamespace stri
// Snapshot location specified
objectStoreProvider = "aws"
}
err := EnsureClusterExists(ctx)
err := ensureClusterExists(ctx)
if err != nil {
return errors.WithMessage(err, "Failed to ensure kubernetes cluster exists")
return errors.WithMessage(err, "Failed to ensure Kubernetes cluster exists")
}
veleroInstallOptions, err := GetProviderVeleroInstallOptions(objectStoreProvider, cloudCredentialsFile, bslBucket,
veleroInstallOptions, err := getProviderVeleroInstallOptions(objectStoreProvider, cloudCredentialsFile, bslBucket,
bslPrefix, bslConfig, vslConfig, providerPlugins, features)
if err != nil {
return errors.WithMessagef(err, "Failed to get Velero InstallOptions for plugin provider %s", objectStoreProvider)
}
if useVolumeSnapshots {
if cloudProvider != "vsphere" {
veleroInstallOptions.UseVolumeSnapshots = true
@@ -302,25 +310,24 @@ func VeleroInstall(ctx context.Context, veleroImage string, veleroNamespace stri
// being an AWS VSL which causes problems)
}
}
if err != nil {
return errors.WithMessagef(err, "Failed to get Velero InstallOptions for plugin provider %s", objectStoreProvider)
}
veleroInstallOptions.UseRestic = !useVolumeSnapshots
veleroInstallOptions.Image = veleroImage
veleroInstallOptions.CRDsVersion = crdsVersion
veleroInstallOptions.Namespace = veleroNamespace
err = InstallVeleroServer(veleroInstallOptions)
err = installVeleroServer(veleroInstallOptions)
if err != nil {
return errors.WithMessagef(err, "Failed to install Velero in cluster")
return errors.WithMessagef(err, "Failed to install Velero in the cluster")
}
return nil
}
func VeleroUninstall(ctx context.Context, client *kubernetes.Clientset, extensionsClient *apiextensionsclient.Clientset, veleroNamespace string) error {
return uninstall.Run(ctx, client, extensionsClient, veleroNamespace, true)
func veleroUninstall(ctx context.Context, client kbclient.Client, installVelero bool, veleroNamespace string) error {
return uninstall.Run(ctx, client, veleroNamespace, true)
}
func VeleroBackupLogs(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string) error {
func veleroBackupLogs(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string) error {
describeCmd := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "backup", "describe", backupName)
describeCmd.Stdout = os.Stdout
describeCmd.Stderr = os.Stderr
@@ -338,7 +345,7 @@ func VeleroBackupLogs(ctx context.Context, veleroCLI string, veleroNamespace str
return nil
}
func VeleroRestoreLogs(ctx context.Context, veleroCLI string, veleroNamespace string, restoreName string) error {
func veleroRestoreLogs(ctx context.Context, veleroCLI string, veleroNamespace string, restoreName string) error {
describeCmd := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "restore", "describe", restoreName)
describeCmd.Stdout = os.Stdout
describeCmd.Stderr = os.Stderr
@@ -356,7 +363,7 @@ func VeleroRestoreLogs(ctx context.Context, veleroCLI string, veleroNamespace st
return nil
}
func VeleroCreateBackupLocation(ctx context.Context,
func veleroCreateBackupLocation(ctx context.Context,
veleroCLI string,
veleroNamespace string,
name string,
@@ -393,16 +400,16 @@ func VeleroCreateBackupLocation(ctx context.Context,
return bslCreateCmd.Run()
}
// VeleroAddPluginsForProvider determines which plugins need to be installed for a provider and
// veleroAddPluginsForProvider determines which plugins need to be installed for a provider and
// installs them in the current Velero installation, skipping over those that are already installed.
func VeleroAddPluginsForProvider(ctx context.Context, veleroCLI string, veleroNamespace string, provider string) error {
func veleroAddPluginsForProvider(ctx context.Context, veleroCLI string, veleroNamespace string, provider string) error {
for _, plugin := range getProviderPlugins(provider) {
stdoutBuf := new(bytes.Buffer)
stderrBuf := new(bytes.Buffer)
installPluginCmd := exec.CommandContext(ctx, veleroCLI, "--namespace", veleroNamespace, "plugin", "add", plugin)
installPluginCmd.Stdout = stdoutBuf
installPluginCmd.Stderr = stdoutBuf
installPluginCmd.Stderr = stderrBuf
err := installPluginCmd.Run()
@@ -421,10 +428,8 @@ func VeleroAddPluginsForProvider(ctx context.Context, veleroCLI string, veleroNa
return nil
}
/*
Waits for uploads started by the Velero Plug-in for vSphere to complete
TODO - remove after upload progress monitoring is implemented
*/
// waitForVSphereUploadCompletion waits for uploads started by the Velero Plug-in for vSphere to complete
// TODO - remove after upload progress monitoring is implemented
func waitForVSphereUploadCompletion(ctx context.Context, timeout time.Duration, namespace string) error {
err := wait.PollImmediate(time.Minute, timeout, func() (bool, error) {
checkSnapshotCmd := exec.CommandContext(ctx, "kubectl",