mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-02-09 05:20:13 +00:00
Compare commits
142 Commits
v1.0.0
...
v1.1.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d9c232729 | ||
|
|
d5dd39c941 | ||
|
|
d881a10fba | ||
|
|
80692a8a39 | ||
|
|
4accb8512a | ||
|
|
4e1b1f9457 | ||
|
|
de442c1106 | ||
|
|
6522ba7c42 | ||
|
|
a590fc9468 | ||
|
|
539e0b54c6 | ||
|
|
a4e70456a1 | ||
|
|
2254635bcb | ||
|
|
07525bd593 | ||
|
|
635dd27e1a | ||
|
|
2a6929d453 | ||
|
|
b24a603711 | ||
|
|
f2d06bc5e9 | ||
|
|
4543258970 | ||
|
|
22eca22ac8 | ||
|
|
6188cdffb0 | ||
|
|
935107e1a2 | ||
|
|
bf247836e6 | ||
|
|
b24e940399 | ||
|
|
248ee89123 | ||
|
|
52d97e7bd7 | ||
|
|
b8f3a008cb | ||
|
|
25a481f6b1 | ||
|
|
3b9af8c654 | ||
|
|
f80e1dc390 | ||
|
|
3c4dd3e526 | ||
|
|
8336d95f57 | ||
|
|
5f409f12c8 | ||
|
|
65906efffa | ||
|
|
c8bac5bfae | ||
|
|
92ac710dab | ||
|
|
c69b94da76 | ||
|
|
927d2775bf | ||
|
|
1594bdc8d0 | ||
|
|
aa8c0cd471 | ||
|
|
3124570c7f | ||
|
|
1d54996fce | ||
|
|
5841f82ee9 | ||
|
|
1c69bafeeb | ||
|
|
8e098e2f6c | ||
|
|
500d5485b1 | ||
|
|
1bb167ef90 | ||
|
|
11194d1071 | ||
|
|
63964fc6f9 | ||
|
|
d615cc6de0 | ||
|
|
8cde8fdbc7 | ||
|
|
ac00185a5f | ||
|
|
31973fbf04 | ||
|
|
d1025f7547 | ||
|
|
678c02c560 | ||
|
|
7724464017 | ||
|
|
f44bd53cf0 | ||
|
|
2498ac6cab | ||
|
|
afa49f398f | ||
|
|
f829dabcf4 | ||
|
|
eec5cc687e | ||
|
|
bf00754280 | ||
|
|
13cac85c1e | ||
|
|
567802299b | ||
|
|
adb93c33b1 | ||
|
|
e371ba78b0 | ||
|
|
2156124dfc | ||
|
|
db393ec199 | ||
|
|
022099a62e | ||
|
|
55054f67a5 | ||
|
|
75a96dfa92 | ||
|
|
81c2adc059 | ||
|
|
dd96aa76db | ||
|
|
0089fa4d93 | ||
|
|
553df25710 | ||
|
|
764de79cf5 | ||
|
|
f4c64ae75a | ||
|
|
c48586a8c7 | ||
|
|
5d8ba1b90d | ||
|
|
1a339f06ac | ||
|
|
b0bdaeea73 | ||
|
|
08fe7be851 | ||
|
|
67512a3808 | ||
|
|
37c7b618ad | ||
|
|
d143137a70 | ||
|
|
5634a4f463 | ||
|
|
19052994ed | ||
|
|
8cb9ee9eb8 | ||
|
|
0d326a3903 | ||
|
|
d916ae0a25 | ||
|
|
b00e0e834a | ||
|
|
d421fcd85c | ||
|
|
0735ee7218 | ||
|
|
223aec8200 | ||
|
|
870743a28d | ||
|
|
1f4139a5bf | ||
|
|
a2e88c4d3f | ||
|
|
2d81e29276 | ||
|
|
5d3312b7b5 | ||
|
|
fac3cd4a78 | ||
|
|
3ff95eaa40 | ||
|
|
023d43d0fe | ||
|
|
b807b449d6 | ||
|
|
1ded7c7207 | ||
|
|
f3850210aa | ||
|
|
e2bf39a027 | ||
|
|
6513e8f30e | ||
|
|
49f52b54b2 | ||
|
|
74b575200c | ||
|
|
520077c3a9 | ||
|
|
e183c4b597 | ||
|
|
f0f7d31e1b | ||
|
|
9a62d887d3 | ||
|
|
d6c60b2dd5 | ||
|
|
9e7ff4e3d9 | ||
|
|
bb12cbd2d7 | ||
|
|
16a08b82a9 | ||
|
|
c539e8ad63 | ||
|
|
f1319be60b | ||
|
|
7d9fc88eb3 | ||
|
|
0a771e6a53 | ||
|
|
bc7ee686d7 | ||
|
|
81a26e4aad | ||
|
|
caa0bff5a3 | ||
|
|
83c3143825 | ||
|
|
81287e4751 | ||
|
|
0804f34644 | ||
|
|
411d44a673 | ||
|
|
4e2e4cd5c4 | ||
|
|
108d826ca5 | ||
|
|
bbb11a8d23 | ||
|
|
82e464672b | ||
|
|
035c297287 | ||
|
|
02095d21d8 | ||
|
|
5f7bab945d | ||
|
|
ddb335475b | ||
|
|
915b3a1ddf | ||
|
|
a1f26aa3a8 | ||
|
|
9287505f62 | ||
|
|
02c7df9ea0 | ||
|
|
13b6cbb4db | ||
|
|
029cee6bc3 | ||
|
|
1ed5255f17 |
@@ -4,6 +4,10 @@ about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
**Describe the problem/challenge you have**
|
||||
[A description of the current limitation/problem/challenge that you are experiencing.]
|
||||
|
||||
|
||||
**Describe the solution you'd like**
|
||||
[A clear and concise description of what you want to happen.]
|
||||
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -43,5 +43,7 @@ site/.sass-cache
|
||||
site/.jekyll
|
||||
site/.jekyll-metadata
|
||||
site/.bundle
|
||||
site/vendor
|
||||
.ruby-version
|
||||
|
||||
.vs
|
||||
|
||||
@@ -25,16 +25,21 @@ builds:
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
- ppc64le
|
||||
ignore:
|
||||
# don't build arm/arm64 for darwin or windows
|
||||
- goos: darwin
|
||||
goarch: arm
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
- goos: darwin
|
||||
goarch: ppc64le
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
goarch: ppc64le
|
||||
ldflags:
|
||||
- -X "github.com/heptio/velero/pkg/buildinfo.Version={{ .Tag }}" -X "github.com/heptio/velero/pkg/buildinfo.GitSHA={{ .FullCommit }}" -X "github.com/heptio/velero/pkg/buildinfo.GitTreeState={{ .Env.GIT_TREE_STATE }}"
|
||||
archive:
|
||||
|
||||
@@ -8,4 +8,4 @@ sudo: required
|
||||
services:
|
||||
- docker
|
||||
|
||||
script: make ci
|
||||
script: hack/ci-check.sh
|
||||
|
||||
19
Dockerfile-fsfreeze-pause-ppc64le
Normal file
19
Dockerfile-fsfreeze-pause-ppc64le
Normal file
@@ -0,0 +1,19 @@
|
||||
# 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.
|
||||
|
||||
FROM ubuntu:bionic
|
||||
|
||||
LABEL maintainer="Steve Kriss <krisss@vmware.com>"
|
||||
|
||||
ENTRYPOINT ["/bin/bash", "-c", "while true; do sleep 10000; done"]
|
||||
32
Dockerfile-velero-ppc64le
Normal file
32
Dockerfile-velero-ppc64le
Normal file
@@ -0,0 +1,32 @@
|
||||
# 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.
|
||||
|
||||
FROM ubuntu:bionic
|
||||
|
||||
LABEL maintainer="Steve Kriss <krisss@vmware.com>"
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends ca-certificates wget && \
|
||||
wget --quiet https://oplab9.parqtec.unicamp.br/pub/ppc64el/restic/restic-0.9.4 && \
|
||||
mv restic-0.9.4 /usr/bin/restic && \
|
||||
chmod +x /usr/bin/restic && \
|
||||
apt-get remove -y wget && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
ADD /bin/linux/ppc64le/velero /velero
|
||||
|
||||
USER nobody:nobody
|
||||
|
||||
ENTRYPOINT ["/velero"]
|
||||
23
Dockerfile-velero-restic-restore-helper-ppc64le
Normal file
23
Dockerfile-velero-restic-restore-helper-ppc64le
Normal file
@@ -0,0 +1,23 @@
|
||||
# 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.
|
||||
|
||||
FROM ubuntu:bionic
|
||||
|
||||
LABEL maintainer="Steve Kriss <krisss@vmware.com>"
|
||||
|
||||
ADD /bin/linux/ppc64le/velero-restic-restore-helper .
|
||||
|
||||
USER nobody:nobody
|
||||
|
||||
ENTRYPOINT [ "/velero-restic-restore-helper" ]
|
||||
258
Gopkg.lock
generated
258
Gopkg.lock
generated
@@ -17,7 +17,7 @@
|
||||
version = "v0.11.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5b71d15be52cbb93f5115f51ace93798204f6b4a3df0992d0b6da8644f505984"
|
||||
digest = "1:623dad7b6ddc6b93f983e9852a0785ed606f804d3541fa4b6178d7055b361306"
|
||||
name = "github.com/Azure/azure-sdk-for-go"
|
||||
packages = [
|
||||
"services/compute/mgmt/2018-04-01/compute",
|
||||
@@ -26,11 +26,11 @@
|
||||
"version",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "520918e6c8e8e1064154f51d13e02fad92b287b8"
|
||||
version = "v19.0.0"
|
||||
revision = "32916f57ad7b421f5fdaab86b73a795632fff117"
|
||||
version = "v21.4.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b825d8578481c8877ff3b9a3654d77a48577cc33e65f33c3678d7e3f134bf73d"
|
||||
digest = "1:90df11ad9349a69d46e08211d47eb8db80311bf985a447dd88cb30d5b5f54add"
|
||||
name = "github.com/Azure/go-autorest"
|
||||
packages = [
|
||||
"autorest",
|
||||
@@ -39,11 +39,12 @@
|
||||
"autorest/date",
|
||||
"autorest/to",
|
||||
"autorest/validation",
|
||||
"logger",
|
||||
"version",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "bca49d5b51a50dc5bb17bbf6204c711c6dbded06"
|
||||
version = "v10.14.0"
|
||||
revision = "1ffcc8896ef6dfe022d90a4317d866f925cf0f9e"
|
||||
version = "v11.1.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:f41188abdb95b92995643a927f5bdd208389822a8e1aba00d85633ae51b85c85"
|
||||
@@ -128,14 +129,6 @@
|
||||
pruneopts = "NUT"
|
||||
revision = "944e07253867aacae43c04b2e6a239005443f33a"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:81466b4218bf6adddac2572a30ac733a9255919bc2f470b4827a317bd4ee1756"
|
||||
name = "github.com/ghodss/yaml"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:021d6ee454d87208dd1cd731cd702d3521aa8a51ad2072fa7beffbb3d677d8bb"
|
||||
name = "github.com/go-ini/ini"
|
||||
@@ -144,6 +137,23 @@
|
||||
revision = "20b96f641a5ea98f2f8619ff4f3e061cff4833bd"
|
||||
version = "v1.28.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:6a7159c5f8f8826207545407a458b43ab6599c1c8a2271465c2e979ecea1dcd4"
|
||||
name = "github.com/gobwas/glob"
|
||||
packages = [
|
||||
".",
|
||||
"compiler",
|
||||
"match",
|
||||
"syntax",
|
||||
"syntax/ast",
|
||||
"syntax/lexer",
|
||||
"util/runes",
|
||||
"util/strings",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "5ccd90ef52e1e632236f7326478d4faa74f99438"
|
||||
version = "v0.2.3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a6afc27b2a73a5506832f3c5a1c19a30772cb69e7bd1ced4639eb36a55db224f"
|
||||
name = "github.com/gogo/protobuf"
|
||||
@@ -155,14 +165,6 @@
|
||||
revision = "100ba4e885062801d56799d78530b73b178a78f3"
|
||||
version = "v0.4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:e2b86e41f3d669fc36b50d31d32d22c8ac656c75aa5ea89717ce7177e134ff2a"
|
||||
name = "github.com/golang/glog"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a98a0b00720dc3149bf3d0c8d5726188899e5bab2f5072b9a7ef82958fbc98b2"
|
||||
name = "github.com/golang/protobuf"
|
||||
@@ -178,14 +180,6 @@
|
||||
revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30"
|
||||
version = "v1.3.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:245bd4eb633039cd66106a5d340ae826d87f4e36a8602fcc940e14176fd26ea7"
|
||||
name = "github.com/google/btree"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:52c5834e2bebac9030c97cc0798ac11c3aa8a39f098aeb419f142533da6cd3cc"
|
||||
@@ -214,17 +208,6 @@
|
||||
revision = "ee43cbb60db7bd22502942cccbc39059117352ab"
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:7fdf3223c7372d1ced0b98bf53457c5e89d89aecbad9a77ba9fcc6e01f9e5621"
|
||||
name = "github.com/gregjones/httpcache"
|
||||
packages = [
|
||||
".",
|
||||
"diskcache",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "9cad4c3443a7200dd6400aef47183728de563a38"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:32e5a56c443b5581e4bf6e74cdc78b5826d7e4c5df43883e2dc31e4d7f4ae98a"
|
||||
@@ -234,14 +217,14 @@
|
||||
revision = "ca137eb4b4389c9bc6f1a6d887f056bf16c00510"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:143aae8d04a6133eea9c6400b90a1f47ae1100b48a1636160aba861d1b26c5b2"
|
||||
digest = "1:980fd2c6afd6c268284d46dd66671771184a4002f6d516492cc596d2ca003543"
|
||||
name = "github.com/hashicorp/go-plugin"
|
||||
packages = [
|
||||
".",
|
||||
"internal/plugin",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "3f118e8ee104b6f22aeb12453fab56aed1356186"
|
||||
revision = "a1bc61569a26c0f65865932c0d55743b0567c494"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -294,12 +277,20 @@
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8e36686e8b139f8fe240c1d5cf3a145bc675c22ff8e707857cdd3ae17b00d728"
|
||||
digest = "1:0243cffa4a3410f161ee613dfdd903a636d07e838a42d341da95d81f42cd1d41"
|
||||
name = "github.com/json-iterator/go"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "1624edc4454b8682399def8740d46db5e4362ba4"
|
||||
version = "v1.1.5"
|
||||
revision = "ab8a2e0c74be9d3be70b3184d9acc634935ded82"
|
||||
version = "1.1.4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:e94e69261097d7067fa28052bcf209be4c47b12c665b7e88116c96f905a77364"
|
||||
name = "github.com/liggitt/tabwriter"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "89fcab3d43de07060e4fd4c1547430ed57e87f24"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:13ada91f079028d1b4ca88e10a16439dcfa6541d26ed2e61e770f56d06301933"
|
||||
@@ -349,22 +340,6 @@
|
||||
revision = "4dadeb3030eda0273a12382bb2348ffc7c9d1a39"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:3bf17a6e6eaa6ad24152148a631d18662f7212e21637c2699bff3369b7f00fa2"
|
||||
name = "github.com/petar/GoLLRB"
|
||||
packages = ["llrb"]
|
||||
pruneopts = "NUT"
|
||||
revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:6c6d91dc326ed6778783cff869c49fb2f61303cdd2ebbcf90abe53505793f3b6"
|
||||
name = "github.com/peterbourgon/diskv"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "5f041e8faa004a95c88a202771f4cc3e991971e6"
|
||||
version = "v2.0.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121"
|
||||
name = "github.com/pkg/errors"
|
||||
@@ -569,18 +544,12 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:0f6792185947c44cd78bc6a2f4399c44c7e85d406b3229a27d41f6cd0a8e982b"
|
||||
digest = "1:97337ef8cb438f9e3a99ea91a300e916ed9a96fbf3ad50f9a020d30ea9f8692f"
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"encoding",
|
||||
"encoding/internal",
|
||||
"encoding/internal/identifier",
|
||||
"encoding/unicode",
|
||||
"internal/gen",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
"internal/utf8internal",
|
||||
"runes",
|
||||
"secure/bidirule",
|
||||
"transform",
|
||||
"unicode/bidi",
|
||||
@@ -709,15 +678,14 @@
|
||||
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:93e9a6515f47aaaf7f1c84617fc8c82db9216f7290c4d4149afeaf6936d9aa5e"
|
||||
digest = "1:a937ed4322409fa22924f02124fd0727c19662f73cf15406646d19bdce972df2"
|
||||
name = "k8s.io/api"
|
||||
packages = [
|
||||
"admission/v1beta1",
|
||||
"admissionregistration/v1alpha1",
|
||||
"admissionregistration/v1beta1",
|
||||
"apps/v1",
|
||||
"apps/v1beta1",
|
||||
"apps/v1beta2",
|
||||
"auditregistration/v1alpha1",
|
||||
"authentication/v1",
|
||||
"authentication/v1beta1",
|
||||
"authorization/v1",
|
||||
@@ -729,16 +697,20 @@
|
||||
"batch/v1beta1",
|
||||
"batch/v2alpha1",
|
||||
"certificates/v1beta1",
|
||||
"coordination/v1",
|
||||
"coordination/v1beta1",
|
||||
"core/v1",
|
||||
"events/v1beta1",
|
||||
"extensions/v1beta1",
|
||||
"imagepolicy/v1alpha1",
|
||||
"networking/v1",
|
||||
"networking/v1beta1",
|
||||
"node/v1alpha1",
|
||||
"node/v1beta1",
|
||||
"policy/v1beta1",
|
||||
"rbac/v1",
|
||||
"rbac/v1alpha1",
|
||||
"rbac/v1beta1",
|
||||
"scheduling/v1",
|
||||
"scheduling/v1alpha1",
|
||||
"scheduling/v1beta1",
|
||||
"settings/v1alpha1",
|
||||
@@ -747,22 +719,22 @@
|
||||
"storage/v1beta1",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "fd83cbc87e7632ccd8bbab63d2b673d4e0c631cc"
|
||||
version = "kubernetes-1.12.0"
|
||||
revision = "40a48860b5abbba9aa891b02b32da429b08d96a0"
|
||||
version = "kubernetes-1.14.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b8a1dcc5f4e559b7af185ba12dd341cb8c175ea3d36227a02699b251ae5fde05"
|
||||
digest = "1:1d6160800196e00fc394f13ca8c1c0cdc360a170c1b6a9db0f0a1f9f1c4e9342"
|
||||
name = "k8s.io/apiextensions-apiserver"
|
||||
packages = [
|
||||
"pkg/apis/apiextensions",
|
||||
"pkg/apis/apiextensions/v1beta1",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "1748dfb29e8a4432b78514bc88a1b07937a9805a"
|
||||
version = "kubernetes-1.12.0"
|
||||
revision = "53c4693659ed354d76121458fb819202dd1635fa"
|
||||
version = "kubernetes-1.14.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ca279c0bb7a72618aff5b77440d5a5e2f92857fdb7e0e4c7a1a77a7895929c49"
|
||||
digest = "1:f249ae79e492647bb0640d656ccf70fd272359a75a35ac5b9748bd19ac42c1f0"
|
||||
name = "k8s.io/apimachinery"
|
||||
packages = [
|
||||
"pkg/api/equality",
|
||||
@@ -772,7 +744,6 @@
|
||||
"pkg/apis/meta/internalversion",
|
||||
"pkg/apis/meta/v1",
|
||||
"pkg/apis/meta/v1/unstructured",
|
||||
"pkg/apis/meta/v1/unstructured/unstructuredscheme",
|
||||
"pkg/apis/meta/v1beta1",
|
||||
"pkg/conversion",
|
||||
"pkg/conversion/queryparams",
|
||||
@@ -816,36 +787,26 @@
|
||||
"third_party/forked/golang/reflect",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "6dd46049f39503a1fc8d65de4bd566829e95faff"
|
||||
version = "kubernetes-1.12.0"
|
||||
revision = "d7deff9243b165ee192f5551710ea4285dcfd615"
|
||||
version = "kubernetes-1.14.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "release-1.12"
|
||||
digest = "1:7991e5074de01462e0cf6ef77060895b50e9026d16152a6e925cb99b67a1f8ae"
|
||||
name = "k8s.io/cli-runtime"
|
||||
packages = [
|
||||
"pkg/genericclioptions",
|
||||
"pkg/genericclioptions/printers",
|
||||
"pkg/genericclioptions/resource",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "11047e25a94a7eaa541b92a8bbfd3e1243607219"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5d9f76731330e62bede1e4eb9d519b282a26621a5368e5db1a18a8eb1ccda1ff"
|
||||
digest = "1:c225d3ffade76b27498a24472eeaa3e7c516f0c0bbaaacfaa2a1824e1ead7952"
|
||||
name = "k8s.io/client-go"
|
||||
packages = [
|
||||
"discovery",
|
||||
"discovery/fake",
|
||||
"dynamic",
|
||||
"dynamic/fake",
|
||||
"informers",
|
||||
"informers/admissionregistration",
|
||||
"informers/admissionregistration/v1alpha1",
|
||||
"informers/admissionregistration/v1beta1",
|
||||
"informers/apps",
|
||||
"informers/apps/v1",
|
||||
"informers/apps/v1beta1",
|
||||
"informers/apps/v1beta2",
|
||||
"informers/auditregistration",
|
||||
"informers/auditregistration/v1alpha1",
|
||||
"informers/autoscaling",
|
||||
"informers/autoscaling/v1",
|
||||
"informers/autoscaling/v2beta1",
|
||||
@@ -857,6 +818,7 @@
|
||||
"informers/certificates",
|
||||
"informers/certificates/v1beta1",
|
||||
"informers/coordination",
|
||||
"informers/coordination/v1",
|
||||
"informers/coordination/v1beta1",
|
||||
"informers/core",
|
||||
"informers/core/v1",
|
||||
@@ -867,6 +829,10 @@
|
||||
"informers/internalinterfaces",
|
||||
"informers/networking",
|
||||
"informers/networking/v1",
|
||||
"informers/networking/v1beta1",
|
||||
"informers/node",
|
||||
"informers/node/v1alpha1",
|
||||
"informers/node/v1beta1",
|
||||
"informers/policy",
|
||||
"informers/policy/v1beta1",
|
||||
"informers/rbac",
|
||||
@@ -874,6 +840,7 @@
|
||||
"informers/rbac/v1alpha1",
|
||||
"informers/rbac/v1beta1",
|
||||
"informers/scheduling",
|
||||
"informers/scheduling/v1",
|
||||
"informers/scheduling/v1alpha1",
|
||||
"informers/scheduling/v1beta1",
|
||||
"informers/settings",
|
||||
@@ -883,43 +850,85 @@
|
||||
"informers/storage/v1alpha1",
|
||||
"informers/storage/v1beta1",
|
||||
"kubernetes",
|
||||
"kubernetes/fake",
|
||||
"kubernetes/scheme",
|
||||
"kubernetes/typed/admissionregistration/v1alpha1",
|
||||
"kubernetes/typed/admissionregistration/v1beta1",
|
||||
"kubernetes/typed/admissionregistration/v1beta1/fake",
|
||||
"kubernetes/typed/apps/v1",
|
||||
"kubernetes/typed/apps/v1/fake",
|
||||
"kubernetes/typed/apps/v1beta1",
|
||||
"kubernetes/typed/apps/v1beta1/fake",
|
||||
"kubernetes/typed/apps/v1beta2",
|
||||
"kubernetes/typed/apps/v1beta2/fake",
|
||||
"kubernetes/typed/auditregistration/v1alpha1",
|
||||
"kubernetes/typed/auditregistration/v1alpha1/fake",
|
||||
"kubernetes/typed/authentication/v1",
|
||||
"kubernetes/typed/authentication/v1/fake",
|
||||
"kubernetes/typed/authentication/v1beta1",
|
||||
"kubernetes/typed/authentication/v1beta1/fake",
|
||||
"kubernetes/typed/authorization/v1",
|
||||
"kubernetes/typed/authorization/v1/fake",
|
||||
"kubernetes/typed/authorization/v1beta1",
|
||||
"kubernetes/typed/authorization/v1beta1/fake",
|
||||
"kubernetes/typed/autoscaling/v1",
|
||||
"kubernetes/typed/autoscaling/v1/fake",
|
||||
"kubernetes/typed/autoscaling/v2beta1",
|
||||
"kubernetes/typed/autoscaling/v2beta1/fake",
|
||||
"kubernetes/typed/autoscaling/v2beta2",
|
||||
"kubernetes/typed/autoscaling/v2beta2/fake",
|
||||
"kubernetes/typed/batch/v1",
|
||||
"kubernetes/typed/batch/v1/fake",
|
||||
"kubernetes/typed/batch/v1beta1",
|
||||
"kubernetes/typed/batch/v1beta1/fake",
|
||||
"kubernetes/typed/batch/v2alpha1",
|
||||
"kubernetes/typed/batch/v2alpha1/fake",
|
||||
"kubernetes/typed/certificates/v1beta1",
|
||||
"kubernetes/typed/certificates/v1beta1/fake",
|
||||
"kubernetes/typed/coordination/v1",
|
||||
"kubernetes/typed/coordination/v1/fake",
|
||||
"kubernetes/typed/coordination/v1beta1",
|
||||
"kubernetes/typed/coordination/v1beta1/fake",
|
||||
"kubernetes/typed/core/v1",
|
||||
"kubernetes/typed/core/v1/fake",
|
||||
"kubernetes/typed/events/v1beta1",
|
||||
"kubernetes/typed/events/v1beta1/fake",
|
||||
"kubernetes/typed/extensions/v1beta1",
|
||||
"kubernetes/typed/extensions/v1beta1/fake",
|
||||
"kubernetes/typed/networking/v1",
|
||||
"kubernetes/typed/networking/v1/fake",
|
||||
"kubernetes/typed/networking/v1beta1",
|
||||
"kubernetes/typed/networking/v1beta1/fake",
|
||||
"kubernetes/typed/node/v1alpha1",
|
||||
"kubernetes/typed/node/v1alpha1/fake",
|
||||
"kubernetes/typed/node/v1beta1",
|
||||
"kubernetes/typed/node/v1beta1/fake",
|
||||
"kubernetes/typed/policy/v1beta1",
|
||||
"kubernetes/typed/policy/v1beta1/fake",
|
||||
"kubernetes/typed/rbac/v1",
|
||||
"kubernetes/typed/rbac/v1/fake",
|
||||
"kubernetes/typed/rbac/v1alpha1",
|
||||
"kubernetes/typed/rbac/v1alpha1/fake",
|
||||
"kubernetes/typed/rbac/v1beta1",
|
||||
"kubernetes/typed/rbac/v1beta1/fake",
|
||||
"kubernetes/typed/scheduling/v1",
|
||||
"kubernetes/typed/scheduling/v1/fake",
|
||||
"kubernetes/typed/scheduling/v1alpha1",
|
||||
"kubernetes/typed/scheduling/v1alpha1/fake",
|
||||
"kubernetes/typed/scheduling/v1beta1",
|
||||
"kubernetes/typed/scheduling/v1beta1/fake",
|
||||
"kubernetes/typed/settings/v1alpha1",
|
||||
"kubernetes/typed/settings/v1alpha1/fake",
|
||||
"kubernetes/typed/storage/v1",
|
||||
"kubernetes/typed/storage/v1/fake",
|
||||
"kubernetes/typed/storage/v1alpha1",
|
||||
"kubernetes/typed/storage/v1alpha1/fake",
|
||||
"kubernetes/typed/storage/v1beta1",
|
||||
"listers/admissionregistration/v1alpha1",
|
||||
"kubernetes/typed/storage/v1beta1/fake",
|
||||
"listers/admissionregistration/v1beta1",
|
||||
"listers/apps/v1",
|
||||
"listers/apps/v1beta1",
|
||||
"listers/apps/v1beta2",
|
||||
"listers/auditregistration/v1alpha1",
|
||||
"listers/autoscaling/v1",
|
||||
"listers/autoscaling/v2beta1",
|
||||
"listers/autoscaling/v2beta2",
|
||||
@@ -927,15 +936,20 @@
|
||||
"listers/batch/v1beta1",
|
||||
"listers/batch/v2alpha1",
|
||||
"listers/certificates/v1beta1",
|
||||
"listers/coordination/v1",
|
||||
"listers/coordination/v1beta1",
|
||||
"listers/core/v1",
|
||||
"listers/events/v1beta1",
|
||||
"listers/extensions/v1beta1",
|
||||
"listers/networking/v1",
|
||||
"listers/networking/v1beta1",
|
||||
"listers/node/v1alpha1",
|
||||
"listers/node/v1beta1",
|
||||
"listers/policy/v1beta1",
|
||||
"listers/rbac/v1",
|
||||
"listers/rbac/v1alpha1",
|
||||
"listers/rbac/v1beta1",
|
||||
"listers/scheduling/v1",
|
||||
"listers/scheduling/v1alpha1",
|
||||
"listers/scheduling/v1beta1",
|
||||
"listers/settings/v1alpha1",
|
||||
@@ -967,20 +981,27 @@
|
||||
"tools/remotecommand",
|
||||
"transport",
|
||||
"transport/spdy",
|
||||
"util/buffer",
|
||||
"util/cert",
|
||||
"util/connrotation",
|
||||
"util/exec",
|
||||
"util/flowcontrol",
|
||||
"util/homedir",
|
||||
"util/integer",
|
||||
"util/jsonpath",
|
||||
"util/keyutil",
|
||||
"util/retry",
|
||||
"util/workqueue",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "1638f8970cefaa404ff3a62950f88b08292b2696"
|
||||
version = "v9.0.0"
|
||||
revision = "6ee68ca5fd8355d024d02f9db0b3b667e8357a0f"
|
||||
version = "kubernetes-1.14.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2c16dda1c44c2564a7818fbacb701323c16d77c21b969987c1bec08d3ee0b050"
|
||||
name = "k8s.io/klog"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "e531227889390a39d9533dde61f590fe9f4b0035"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -991,15 +1012,32 @@
|
||||
revision = "d83b052f768a50a309c692a9c271da3f3276ff88"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8a9b1e755afd7ea778cd451a955977eb3fe0abcc4e32079644b6b7afc42d7ff8"
|
||||
digest = "1:c2ad4e18f35cf651af430e4115e9d26bdd266e61b4076cb76d23a15078c5d58e"
|
||||
name = "k8s.io/kubernetes"
|
||||
packages = ["pkg/printers"]
|
||||
pruneopts = "NUT"
|
||||
revision = "b7394102d6ef778017f2ca4046abbaa23b88c290"
|
||||
version = "v1.14.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:14e8a3b53e6d8cb5f44783056b71bb2ca1ac7e333939cc97f3e50b579c920845"
|
||||
name = "k8s.io/utils"
|
||||
packages = [
|
||||
"pkg/kubectl/scheme",
|
||||
"pkg/printers",
|
||||
"buffer",
|
||||
"integer",
|
||||
"trace",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "51dd616cdd25d6ee22c83a858773b607328a18ec"
|
||||
version = "v1.12.5"
|
||||
revision = "21c4ce38f2a793ec01e925ddc31216500183b773"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8730e0150dfb2b7e173890c8b9868e7a273082ef8e39f4940e3506a481cf895c"
|
||||
name = "sigs.k8s.io/yaml"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "fd68e9863619f6ec2fdd8625fe1f02e7c877e480"
|
||||
version = "v1.1.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
@@ -1024,7 +1062,7 @@
|
||||
"github.com/aws/aws-sdk-go/service/s3",
|
||||
"github.com/aws/aws-sdk-go/service/s3/s3manager",
|
||||
"github.com/evanphx/json-patch",
|
||||
"github.com/golang/glog",
|
||||
"github.com/gobwas/glob",
|
||||
"github.com/golang/protobuf/proto",
|
||||
"github.com/hashicorp/go-hclog",
|
||||
"github.com/hashicorp/go-plugin",
|
||||
@@ -1069,6 +1107,7 @@
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer",
|
||||
"k8s.io/apimachinery/pkg/types",
|
||||
"k8s.io/apimachinery/pkg/util/clock",
|
||||
"k8s.io/apimachinery/pkg/util/diff",
|
||||
"k8s.io/apimachinery/pkg/util/duration",
|
||||
"k8s.io/apimachinery/pkg/util/errors",
|
||||
"k8s.io/apimachinery/pkg/util/runtime",
|
||||
@@ -1079,9 +1118,11 @@
|
||||
"k8s.io/client-go/discovery",
|
||||
"k8s.io/client-go/discovery/fake",
|
||||
"k8s.io/client-go/dynamic",
|
||||
"k8s.io/client-go/dynamic/fake",
|
||||
"k8s.io/client-go/informers",
|
||||
"k8s.io/client-go/informers/core/v1",
|
||||
"k8s.io/client-go/kubernetes",
|
||||
"k8s.io/client-go/kubernetes/fake",
|
||||
"k8s.io/client-go/kubernetes/scheme",
|
||||
"k8s.io/client-go/kubernetes/typed/core/v1",
|
||||
"k8s.io/client-go/kubernetes/typed/rbac/v1",
|
||||
@@ -1098,6 +1139,7 @@
|
||||
"k8s.io/client-go/tools/remotecommand",
|
||||
"k8s.io/client-go/util/flowcontrol",
|
||||
"k8s.io/client-go/util/workqueue",
|
||||
"k8s.io/klog",
|
||||
"k8s.io/kubernetes/pkg/printers",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
|
||||
30
Gopkg.toml
30
Gopkg.toml
@@ -31,28 +31,28 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/kubernetes"
|
||||
version = "~1.12"
|
||||
version = "~1.14"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/client-go"
|
||||
version = "~9.0"
|
||||
version = "kubernetes-1.14.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/apimachinery"
|
||||
version = "kubernetes-1.12.0"
|
||||
version = "kubernetes-1.14.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/api"
|
||||
version = "kubernetes-1.12.0"
|
||||
version = "kubernetes-1.14.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/apiextensions-apiserver"
|
||||
version = "kubernetes-1.12.0"
|
||||
version = "kubernetes-1.14.0"
|
||||
|
||||
# k8s.io/client-go v9.0 uses f2b4162afba35581b6d4a50d3b8f34e33c144682 (released in v1.1.4)
|
||||
# k8s.io/client-go kubernetes-1.14.0 uses v1.1.4
|
||||
[[override]]
|
||||
name = "github.com/json-iterator/go"
|
||||
version = "~1.1.4"
|
||||
version = "=1.1.4"
|
||||
|
||||
#
|
||||
# Cloud provider packages
|
||||
@@ -63,12 +63,12 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/Azure/azure-sdk-for-go"
|
||||
version = "~19.0.0"
|
||||
version = "~21.4.0"
|
||||
|
||||
# k8s.io/client-go v9.0 uses bca49d5b51a50dc5bb17bbf6204c711c6dbded06 (v10.14.0)
|
||||
# k8s.io/client-go kubernetes-1.14.0 uses v11.1.2
|
||||
[[constraint]]
|
||||
name = "github.com/Azure/go-autorest"
|
||||
version = "~10.14.0"
|
||||
version = "11.1.2"
|
||||
|
||||
[[constraint]]
|
||||
name = "cloud.google.com/go"
|
||||
@@ -85,10 +85,6 @@
|
||||
#
|
||||
# Third party packages
|
||||
#
|
||||
[[constraint]]
|
||||
name = "github.com/golang/glog"
|
||||
branch = "master"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/robfig/cron"
|
||||
revision = "df38d32658d8788cd446ba74db4bb5375c4b0cb3"
|
||||
@@ -115,7 +111,7 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/hashicorp/go-plugin"
|
||||
revision = "3f118e8ee104b6f22aeb12453fab56aed1356186"
|
||||
revision = "a1bc61569a26c0f65865932c0d55743b0567c494"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/golang/protobuf"
|
||||
@@ -129,6 +125,10 @@
|
||||
name = "github.com/joho/godotenv"
|
||||
version = "~v1.3.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gobwas/glob"
|
||||
version = "~v0.2.3"
|
||||
|
||||
[[override]]
|
||||
name = "golang.org/x/sys"
|
||||
branch = "master"
|
||||
|
||||
41
Makefile
41
Makefile
@@ -37,8 +37,8 @@ TAG_LATEST ?= false
|
||||
### These variables should not need tweaking.
|
||||
###
|
||||
|
||||
CLI_PLATFORMS := linux-amd64 linux-arm linux-arm64 darwin-amd64 windows-amd64
|
||||
CONTAINER_PLATFORMS := linux-amd64 linux-arm linux-arm64
|
||||
CLI_PLATFORMS := linux-amd64 linux-arm linux-arm64 darwin-amd64 windows-amd64 linux-ppc64le
|
||||
CONTAINER_PLATFORMS := linux-amd64 linux-arm linux-arm64 linux-ppc64le
|
||||
|
||||
platform_temp = $(subst -, ,$(ARCH))
|
||||
GOOS = $(word 1, $(platform_temp))
|
||||
@@ -55,6 +55,9 @@ endif
|
||||
#ifeq ($(GOARCH),arm64)
|
||||
# DOCKERFILE ?= Dockerfile.arm64 #aarch64/busybox
|
||||
#endif
|
||||
ifeq ($(GOARCH),ppc64le)
|
||||
DOCKERFILE ?= Dockerfile-$(BIN)-ppc64le
|
||||
endif
|
||||
|
||||
IMAGE = $(REGISTRY)/$(BIN)
|
||||
|
||||
@@ -214,3 +217,37 @@ changelog:
|
||||
|
||||
release:
|
||||
hack/goreleaser.sh
|
||||
|
||||
serve-docs:
|
||||
docker run \
|
||||
--rm \
|
||||
-v "$$(pwd)/site:/srv/jekyll" \
|
||||
-it -p 4000:4000 \
|
||||
jekyll/jekyll \
|
||||
jekyll serve --livereload --incremental
|
||||
|
||||
# gen-docs generates a new versioned docs directory under site/docs. It follows
|
||||
# the following process:
|
||||
# 1. Copies the contents of the most recently tagged docs directory into the new
|
||||
# directory, to establish a useful baseline to diff against.
|
||||
# 2. Adds all copied content from step 1 to git's staging area via 'git add'.
|
||||
# 3. Replaces the contents of the new docs directory with the contents of the
|
||||
# 'master' docs directory, updating any version-specific links (e.g. to a
|
||||
# specific branch of the GitHub repository) to use the new version
|
||||
# 4. Copies the previous version's ToC file and runs 'git add' to establish
|
||||
# a useful baseline to diff against.
|
||||
# 5. Replaces the content of the new ToC file with the master ToC.
|
||||
# 6. Update site/_config.yml and site/_data/toc-mapping.yml to include entries
|
||||
# for the new version.
|
||||
#
|
||||
# The unstaged changes in the working directory can now easily be diff'ed against the
|
||||
# staged changes using 'git diff' to review all docs changes made since the previous
|
||||
# tagged version. Once the unstaged changes are ready, they can be added to the
|
||||
# staging area using 'git add' and then committed.
|
||||
#
|
||||
# To run gen-docs: "NEW_DOCS_VERSION=v1.1.0 make gen-docs"
|
||||
#
|
||||
# **NOTE**: there are additional manual steps required to finalize the process of generating
|
||||
# a new versioned docs site. The full process is documented in site/README-JEKYLL.md.
|
||||
gen-docs:
|
||||
@hack/gen-docs.sh
|
||||
|
||||
18
README.md
18
README.md
@@ -7,8 +7,8 @@
|
||||
Velero (formerly Heptio Ark) gives you tools to back up and restore your Kubernetes cluster resources and persistent volumes. Velero lets you:
|
||||
|
||||
* Take backups of your cluster and restore in case of loss.
|
||||
* Copy cluster resources to other clusters.
|
||||
* Replicate your production environment for development and testing environments.
|
||||
* Migrate cluster resources to other clusters.
|
||||
* Replicate your production cluster to development and testing clusters.
|
||||
|
||||
Velero consists of:
|
||||
|
||||
@@ -19,8 +19,8 @@ You can run Velero in clusters on a cloud provider or on-premises. For detailed
|
||||
|
||||
## Installation
|
||||
|
||||
We strongly recommend that you use an [official release][6] of Velero. The tarballs for each release contain the
|
||||
`velero` command-line client. Follow the instructions under the **Install** section of [our documentation][29] to get started.
|
||||
We strongly recommend that you use an [official release][6] of Velero. The tarballs for each release contain the
|
||||
`velero` command-line client. Follow the [installation instructions][28] to get started.
|
||||
|
||||
_The code and sample YAML files in the master branch of the Velero repository are under active development and are not guaranteed to be stable. Use them at your own risk!_
|
||||
|
||||
@@ -70,11 +70,11 @@ See [the list of releases][6] to find out about feature changes.
|
||||
|
||||
[24]: https://groups.google.com/forum/#!forum/projectvelero
|
||||
[25]: https://kubernetes.slack.com/messages/velero
|
||||
[26]: https://velero.io/docs/v1.0.0/zenhub
|
||||
[26]: https://velero.io/docs/zenhub
|
||||
|
||||
[28]: https://velero.io/docs/install-overview
|
||||
[29]: https://velero.io/docs/
|
||||
[30]: https://velero.io/docs/troubleshooting
|
||||
|
||||
[29]: https://velero.io/docs/v1.0.0/
|
||||
[30]: https://velero.io/docs/v1.0.0/troubleshooting
|
||||
|
||||
[99]: https://velero.io/docs/v1.0.0/support-matrix
|
||||
[99]: https://velero.io/docs/support-matrix
|
||||
[100]: /site/docs/master/img/velero.png
|
||||
|
||||
50
changelogs/CHANGELOG-1.1.md
Normal file
50
changelogs/CHANGELOG-1.1.md
Normal file
@@ -0,0 +1,50 @@
|
||||
## v1.1.0-beta.1
|
||||
#### 2019-08-07
|
||||
|
||||
### Download
|
||||
- https://github.com/heptio/velero/releases/tag/v1.1.0-beta.1
|
||||
|
||||
### Container Image
|
||||
`gcr.io/heptio-images/velero:v1.1.0-beta.1`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.1.0-beta.1/
|
||||
|
||||
|
||||
### All Changes
|
||||
|
||||
* adds the ability to define custom tags to be added to snapshots by specifying custom labels on the Backup CR with the `velero backup create --labels` flag (#1729, @prydonius)
|
||||
* Restore restic volumes from PodVolumeBackups CRs (#1723, @carlisia)
|
||||
* properly restore PVs backed up with restic and a reclaim policy of "Retain" (#1713, @skriss)
|
||||
* Make `--secret-file` flag on `velero install` optional, add `--no-secret` flag for explicit confirmation (#1699, @nrb)
|
||||
* Add low cpu/memory limits to the restic init container. This allows for restoration into namespaces with quotas defined. (#1677, @nrb)
|
||||
* Adds configurable CPU/memory requests and limits to the restic DaemonSet generated by velero install. (#1710, @prydonius)
|
||||
* remove any stale locks from restic repositories every 5m (#1708, @skriss)
|
||||
* error if backup storage location's Bucket field also contains a prefix, and gracefully handle leading/trailing slashes on Bucket and Prefix fields. (#1694, @skriss)
|
||||
* enhancement: allow option to choose JSON log output (#1654, @carthewd)
|
||||
* Adds configurable CPU/memory requests and limits to the Velero Deployment generated by velero install. (#1678, @prydonius)
|
||||
* Store restic PodVolumeBackups in obj storage & use that as source of truth like regular backups. (#1577, @carlisia)
|
||||
* Update Velero Deployment to use apps/v1 API group. `velero install` and `velero plugin add/remove` commands will now require Kubernetes 1.9+ (#1673, @nrb)
|
||||
* Respect the --kubecontext and --kubeconfig arugments for `velero install`. (#1656, @nrb)
|
||||
* add plugin for updating PV & PVC storage classes on restore based on a config map (#1621, @skriss)
|
||||
* Add restic support for CSI volumes (#1615, @nrb)
|
||||
* bug fix: Fixed namespace usage with cli command 'version' (#1630, @jwmatthews)
|
||||
* enhancement: allow users to specify additional Velero/Restic pod annotations on the command line with the pod-annotations flag. (#1626, @tlkamp)
|
||||
* adds validation for pod volumes hostPath mount on restic server startup (#1616, @prydonius)
|
||||
* enable support for ppc64le architecture (#1605, @prajyot)
|
||||
* bug fix: only restore additional items returned from restore item actions if they match the restore's namespace/resource selectors (#1612, @skriss)
|
||||
* add startTimestamp and completionTimestamp to PodVolumeBackup and PodVolumeRestore status fields (#1609, @prydonius)
|
||||
* bug fix: respect namespace selector when determining which restore item actions to run (#1607, @skriss)
|
||||
* ensure correct backup item actions run with namespace selector (#1601, @prydonius)
|
||||
* allows excluding resources from backups with the `velero.io/exclude-from-backup=true` label (#1588, @prydonius)
|
||||
* ensures backup item action modifications to an item's namespace/name are saved in the file path in the tarball (#1587, @prydonius)
|
||||
* Hides `velero server` and `velero restic server` commands from the list of available commands as these are not intended for use by the velero CLI user. (#1561, @prydonius)
|
||||
* remove dependency on glog, update to klog (#1559, @skriss)
|
||||
* move issue-template-gen from docs/ to hack/ (#1558, @skriss)
|
||||
* fix panic when processing DeleteBackupRequest objects without labels (#1556, @prydonius)
|
||||
* support for multiple AWS profiles (#1548, @pranavgaikwad)
|
||||
* Add CLI command to list (get) all Velero plugins (#1535, @carlisia)
|
||||
* Added author as a tag on blog post. Should fix 404 error when trying to follow link as specified in issue #1522. (#1522, @coonsd)
|
||||
* Allow individual backup storage locations to be read-only (#1517, @skriss)
|
||||
* Stop returning an error when a restic volume is empty since it is a valid scenario. (#1480, @carlisia)
|
||||
* add ability to use wildcard in includes/excludes (#1428, @guilhem)
|
||||
1
changelogs/unreleased/1428-guilhem
Normal file
1
changelogs/unreleased/1428-guilhem
Normal file
@@ -0,0 +1 @@
|
||||
add ability to use wildcard in includes/excludes
|
||||
1
changelogs/unreleased/1480-carlisia.md
Normal file
1
changelogs/unreleased/1480-carlisia.md
Normal file
@@ -0,0 +1 @@
|
||||
Stop returning an error when a restic volume is empty since it is a valid scenario.
|
||||
1
changelogs/unreleased/1517-skriss
Normal file
1
changelogs/unreleased/1517-skriss
Normal file
@@ -0,0 +1 @@
|
||||
Allow individual backup storage locations to be read-only
|
||||
1
changelogs/unreleased/1522-coonsd
Normal file
1
changelogs/unreleased/1522-coonsd
Normal file
@@ -0,0 +1 @@
|
||||
Added author as a tag on blog post. Should fix 404 error when trying to follow link as specified in issue #1522.
|
||||
1
changelogs/unreleased/1535-carlisia
Normal file
1
changelogs/unreleased/1535-carlisia
Normal file
@@ -0,0 +1 @@
|
||||
Add CLI command to list (get) all Velero plugins
|
||||
1
changelogs/unreleased/1548-pranavgaikwad
Normal file
1
changelogs/unreleased/1548-pranavgaikwad
Normal file
@@ -0,0 +1 @@
|
||||
support for multiple AWS profiles
|
||||
1
changelogs/unreleased/1556-prydonius
Normal file
1
changelogs/unreleased/1556-prydonius
Normal file
@@ -0,0 +1 @@
|
||||
fix panic when processing DeleteBackupRequest objects without labels
|
||||
1
changelogs/unreleased/1558-skriss
Normal file
1
changelogs/unreleased/1558-skriss
Normal file
@@ -0,0 +1 @@
|
||||
move issue-template-gen from docs/ to hack/
|
||||
1
changelogs/unreleased/1559-skriss
Normal file
1
changelogs/unreleased/1559-skriss
Normal file
@@ -0,0 +1 @@
|
||||
remove dependency on glog, update to klog
|
||||
1
changelogs/unreleased/1561-prydonius
Normal file
1
changelogs/unreleased/1561-prydonius
Normal file
@@ -0,0 +1 @@
|
||||
Hides `velero server` and `velero restic server` commands from the list of available commands as these are not intended for use by the velero CLI user.
|
||||
1
changelogs/unreleased/1577-carlisia
Normal file
1
changelogs/unreleased/1577-carlisia
Normal file
@@ -0,0 +1 @@
|
||||
Store restic PodVolumeBackups in obj storage & use that as source of truth like regular backups.
|
||||
1
changelogs/unreleased/1587-prydonius
Normal file
1
changelogs/unreleased/1587-prydonius
Normal file
@@ -0,0 +1 @@
|
||||
ensures backup item action modifications to an item's namespace/name are saved in the file path in the tarball
|
||||
1
changelogs/unreleased/1588-prydonius
Normal file
1
changelogs/unreleased/1588-prydonius
Normal file
@@ -0,0 +1 @@
|
||||
allows excluding resources from backups with the velero.io/exclude-from-backup=true label
|
||||
1
changelogs/unreleased/1601-prydonius
Normal file
1
changelogs/unreleased/1601-prydonius
Normal file
@@ -0,0 +1 @@
|
||||
ensure correct backup item actions run with namespace selector
|
||||
1
changelogs/unreleased/1605-prajyot-parab
Normal file
1
changelogs/unreleased/1605-prajyot-parab
Normal file
@@ -0,0 +1 @@
|
||||
enable support for ppc64le architecture
|
||||
1
changelogs/unreleased/1607-skriss
Normal file
1
changelogs/unreleased/1607-skriss
Normal file
@@ -0,0 +1 @@
|
||||
bug fix: respect namespace selector when determining which restore item actions to run
|
||||
1
changelogs/unreleased/1609-prydonius
Normal file
1
changelogs/unreleased/1609-prydonius
Normal file
@@ -0,0 +1 @@
|
||||
add startTimestamp and completionTimestamp to PodVolumeBackup and PodVolumeRestore status fields
|
||||
1
changelogs/unreleased/1612-skriss
Normal file
1
changelogs/unreleased/1612-skriss
Normal file
@@ -0,0 +1 @@
|
||||
bug fix: only restore additional items returned from restore item actions if they match the restore's namespace/resource selectors
|
||||
1
changelogs/unreleased/1615-nrb
Normal file
1
changelogs/unreleased/1615-nrb
Normal file
@@ -0,0 +1 @@
|
||||
Add restic support for CSI volumes
|
||||
1
changelogs/unreleased/1616-prydonius
Normal file
1
changelogs/unreleased/1616-prydonius
Normal file
@@ -0,0 +1 @@
|
||||
adds validation for pod volumes hostPath mount on restic server startup
|
||||
1
changelogs/unreleased/1621-skriss
Normal file
1
changelogs/unreleased/1621-skriss
Normal file
@@ -0,0 +1 @@
|
||||
add plugin for updating PV & PVC storage classes on restore based on a config map
|
||||
1
changelogs/unreleased/1626-tlkamp
Normal file
1
changelogs/unreleased/1626-tlkamp
Normal file
@@ -0,0 +1 @@
|
||||
enhancement: allow users to specify additional Velero/Restic pod annotations on the command line with the pod-annotations flag.
|
||||
1
changelogs/unreleased/1630-jwmatthews
Normal file
1
changelogs/unreleased/1630-jwmatthews
Normal file
@@ -0,0 +1 @@
|
||||
bug fix: Fixed namespace usage with cli command 'version'
|
||||
1
changelogs/unreleased/1654-carthewd
Normal file
1
changelogs/unreleased/1654-carthewd
Normal file
@@ -0,0 +1 @@
|
||||
enhancement: allow option to choose JSON log output
|
||||
1
changelogs/unreleased/1656-nrb
Normal file
1
changelogs/unreleased/1656-nrb
Normal file
@@ -0,0 +1 @@
|
||||
Respect the --kubecontext and --kubeconfig arugments for `velero install`.
|
||||
1
changelogs/unreleased/1673-nrb
Normal file
1
changelogs/unreleased/1673-nrb
Normal file
@@ -0,0 +1 @@
|
||||
Update Velero Deployment to use apps/v1 API group. `velero install` and `velero plugin add/remove` commands will now require Kubernetes 1.9+
|
||||
1
changelogs/unreleased/1677-nrb
Normal file
1
changelogs/unreleased/1677-nrb
Normal file
@@ -0,0 +1 @@
|
||||
Add low cpu/memory limits to the restic init container. This allows for restoration into namespaces with quotas defined.
|
||||
1
changelogs/unreleased/1678-prydonius
Normal file
1
changelogs/unreleased/1678-prydonius
Normal file
@@ -0,0 +1 @@
|
||||
Adds configurable CPU/memory requests and limits to the Velero Deployment generated by velero install.
|
||||
1
changelogs/unreleased/1694-skriss
Normal file
1
changelogs/unreleased/1694-skriss
Normal file
@@ -0,0 +1 @@
|
||||
error if backup storage location's Bucket field also contains a prefix, and gracefully handle leading/trailing slashes on Bucket and Prefix fields.
|
||||
1
changelogs/unreleased/1699-nrb
Normal file
1
changelogs/unreleased/1699-nrb
Normal file
@@ -0,0 +1 @@
|
||||
Make --secret-file argument on `velero install` optional, add --no-secret flag for explicit confirmation
|
||||
1
changelogs/unreleased/1708-skriss
Normal file
1
changelogs/unreleased/1708-skriss
Normal file
@@ -0,0 +1 @@
|
||||
remove any stale locks from restic repositories every 5m
|
||||
1
changelogs/unreleased/1710-prydonius
Normal file
1
changelogs/unreleased/1710-prydonius
Normal file
@@ -0,0 +1 @@
|
||||
Adds configurable CPU/memory requests and limits to the restic DaemonSet generated by velero install.
|
||||
1
changelogs/unreleased/1713-skriss
Normal file
1
changelogs/unreleased/1713-skriss
Normal file
@@ -0,0 +1 @@
|
||||
properly restore PVs backed up with restic and a reclaim policy of "Retain"
|
||||
1
changelogs/unreleased/1723-carlisia
Normal file
1
changelogs/unreleased/1723-carlisia
Normal file
@@ -0,0 +1 @@
|
||||
Restore restic volumes from PodVolumeBackups CRs
|
||||
1
changelogs/unreleased/1729-prydonius
Normal file
1
changelogs/unreleased/1729-prydonius
Normal file
@@ -0,0 +1 @@
|
||||
adds the ability to define custom tags to be added to snapshots by specifying custom labels on the Backup CR with the velero backup create --labels flag
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 the Velero contributors.
|
||||
Copyright 2017, 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.
|
||||
@@ -20,14 +20,14 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/klog"
|
||||
|
||||
"github.com/heptio/velero/pkg/cmd"
|
||||
"github.com/heptio/velero/pkg/cmd/velero"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer glog.Flush()
|
||||
defer klog.Flush()
|
||||
|
||||
baseName := filepath.Base(os.Args[0])
|
||||
|
||||
|
||||
48
design/_template.md
Normal file
48
design/_template.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Design proposal template (replace with your proposal's title)
|
||||
|
||||
Status: {Draft,Accepted,Declined}
|
||||
|
||||
One to two sentences that describes the goal of this proposal.
|
||||
The reader should be able to tell by the title, and the opening paragraph, if this document is relevant to them.
|
||||
|
||||
_Note_: The preferred style for design documents is one sentence per line.
|
||||
*Do not wrap lines*.
|
||||
This aids in review of the document as changes to a line are not obscured by the reflowing those changes caused and has a side effect of avoiding debate about one or two space after a period.
|
||||
|
||||
## Goals
|
||||
|
||||
- A short list of things which will be accomplished by implementing this proposal.
|
||||
- Two things is ok.
|
||||
- Three is pushing it.
|
||||
- More than three goals suggests that the proposal's scope is too large.
|
||||
|
||||
## Non Goals
|
||||
|
||||
- A short list of items which are:
|
||||
- a. out of scope
|
||||
- b. follow on items which are deliberately excluded from this proposal.
|
||||
|
||||
## Background
|
||||
|
||||
One to two paragraphs of exposition to set the context for this proposal.
|
||||
|
||||
## High-Level Design
|
||||
|
||||
One to two paragraphs that describe the high level changes that will be made to implement this proposal.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
A detailed design describing how the changes to the product should be made.
|
||||
|
||||
The names of types, fields, interfaces, and methods should be agreed on here, not debated in code review.
|
||||
The same applies to changes in CRDs, YAML examples, and so on.
|
||||
|
||||
Ideally the changes should be made in sequence so that the work required to implement this design can be done incrementally, possibly in parallel.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
If there are alternative high level or detailed designs that were not pursued they should be called out here with a brief explanation of why they were not pursued.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
If this proposal has an impact to the security of the product, its users, or data stored or transmitted via the product, they must be addressed here.
|
||||
96
design/backup-resource-list.md
Normal file
96
design/backup-resource-list.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Expose list of backed up resources in backup details
|
||||
|
||||
Status: Accepted
|
||||
|
||||
To increase the visibility of what a backup might contain, this document proposes storing metadata about backed up resources in object storage and adding a new section to the detailed backup description output to list them.
|
||||
|
||||
## Goals
|
||||
|
||||
- Include a list of backed up resources as metadata in the bucket
|
||||
- Enable users to get a view of what resources are included in a backup using the Velero CLI
|
||||
|
||||
## Non Goals
|
||||
|
||||
- Expose the full manifests of the backed up resources
|
||||
|
||||
## Background
|
||||
|
||||
As reported in [#396](https://github.com/heptio/velero/issues/396), the information reported in a `velero backup describe <name> --details` command is fairly limited, and does not easily describe what resources a backup contains.
|
||||
In order to see what a backup might contain, a user would have to download the backup tarball and extract it.
|
||||
This makes it difficult to keep track of different backups in a cluster.
|
||||
|
||||
## High-Level Design
|
||||
|
||||
After performing a backup, a new file will be created that contains the list of the resources that have been included in the backup.
|
||||
This file will be persisted in object storage alongside the backup contents and existing metadata.
|
||||
|
||||
A section will be added to the output of `velero backup describe <name> --details` command to view this metadata.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### Metadata file
|
||||
|
||||
This metadata will be in JSON (or YAML) format so that it can be easily inspected from the bucket outside of Velero tooling, and will contain the API resource and group, namespaces and names of the resources:
|
||||
|
||||
```
|
||||
apps/v1/Deployment:
|
||||
- default/database
|
||||
- default/wordpress
|
||||
v1/Service:
|
||||
- default/database
|
||||
- default/wordpress
|
||||
v1/Secret:
|
||||
- default/database-root-password
|
||||
- default/database-user-password
|
||||
v1/ConfigMap:
|
||||
- default/database
|
||||
v1/PersistentVolume:
|
||||
- my-pv
|
||||
```
|
||||
|
||||
The filename for this metadata will be `<backup name>-resource-list.json.gz`.
|
||||
The top-level key is the string form of the `schema.GroupResource` type that we currently keep track of in the backup controller code path.
|
||||
|
||||
### Changes in Backup controller
|
||||
|
||||
The Backupper currently initialises a map to track the `backedUpItems` (https://github.com/heptio/velero/blob/1594bdc8d0132f548e18ffcc1db8c4cd2b042726/pkg/backup/backup.go#L269), this is passed down through GroupBackupper, ResourceBackupper and ItemBackupper where ItemBackupper records each backed up item.
|
||||
This property will be moved to the [Backup request struct](https://github.com/heptio/velero/blob/16910a6215cbd8f0bde385dba9879629ebcbcc28/pkg/backup/request.go#L11), allowing the BackupController to access it after a successful backup.
|
||||
|
||||
`backedUpItems` currently uses the `schema.GroupResource` as a key for the resource.
|
||||
In order to record the API group, version and kind for the resource, this key will be constructed from the object's `schema.GroupVersionKind` in the format `{group}/{version}/{kind}` (e.g. `apps/v1/Deployment`).
|
||||
|
||||
The `backedUpItems` map is kept as a flat structure internally for quick lookup.
|
||||
When the backup is ready to upload, `backedUpItems` will be converted to a nested structure representing the metadata file above, grouped by `schema.GroupVersionKind`.
|
||||
After converting to the right format, it can be passed to the `persistBackup` function to persist the file in object storage.
|
||||
|
||||
### Changes to DownloadRequest CRD and processing
|
||||
|
||||
A new `DownloadTargetKind` "BackupResourceList" will be added to the DownloadRequest CR.
|
||||
|
||||
The `GetDownloadURL` function in the `persistence` package will be updated to handle this new DownloadTargetKind to enable the Velero client to fetch the metadata from the bucket.
|
||||
|
||||
### Changes to `velero backup describe <name> --details`
|
||||
|
||||
This command will need to be updated to fetch the metadata from the bucket using the `Stream` method used in other commands.
|
||||
The file will be read in memory and displayed in the output of the command.
|
||||
Depending on the format the metadata is stored in, it may need processing to print in a more human-readable format.
|
||||
If we choose to store the metadata in YAML, it can likely be directly printed out.
|
||||
|
||||
If the metadata file does not exist, this is an older backup and we cannot display the list of resources that were backed up.
|
||||
|
||||
## Open Questions
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Fetch backup contents archive and walkthrough to list contents
|
||||
|
||||
Instead of recording new metadata about what resources have been backed up, we could simply download the backup contents archive and walkthrough it to list the contents everytime `velero backup describe <name> --details` is run.
|
||||
|
||||
The advantage of this approach is that we don't need to change any backup procedures as we already have this content, and we will also be able to list resources for older backups.
|
||||
Additionally, if we wanted to expose more information about the backed up resources, we can do so without having to update what we store in the metadata.
|
||||
|
||||
The disadvantages are:
|
||||
- downloading the whole backup archive will be larger than just downloading a smaller file with metadata
|
||||
- reduces the metadata available in the bucket that users might want to inspect outside of Velero tooling (though this is not an explicit requirement)
|
||||
|
||||
## Security Considerations
|
||||
@@ -19,7 +19,7 @@ metadata:
|
||||
name: velero
|
||||
|
||||
---
|
||||
apiVersion: apps/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
namespace: velero
|
||||
@@ -29,6 +29,9 @@ metadata:
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
component: minio
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
|
||||
@@ -21,13 +21,16 @@ metadata:
|
||||
app: nginx
|
||||
|
||||
---
|
||||
apiVersion: apps/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
namespace: nginx-example
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
|
||||
@@ -29,7 +29,7 @@ metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
storageClassName: <YOUR_STORAGE_CLASS_NAME>
|
||||
# storageClassName: <YOUR_STORAGE_CLASS_NAME>
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
@@ -37,13 +37,16 @@ spec:
|
||||
storage: 50Mi
|
||||
|
||||
---
|
||||
apiVersion: apps/v1beta1
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
namespace: nginx-example
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
|
||||
@@ -17,8 +17,8 @@ FROM golang:1.12
|
||||
RUN mkdir -p /go/src/k8s.io && \
|
||||
cd /go/src/k8s.io && \
|
||||
git config --global advice.detachedHead false && \
|
||||
git clone -b kubernetes-1.12.0 https://github.com/kubernetes/code-generator && \
|
||||
git clone -b kubernetes-1.12.0 https://github.com/kubernetes/apimachinery && \
|
||||
git clone -b kubernetes-1.14.0 https://github.com/kubernetes/code-generator && \
|
||||
git clone -b kubernetes-1.14.0 https://github.com/kubernetes/apimachinery && \
|
||||
go get golang.org/x/tools/cmd/goimports && \
|
||||
cd /go/src/golang.org/x/tools && \
|
||||
git checkout 40a48ad93fbe707101afb2099b738471f70594ec && \
|
||||
|
||||
17
hack/ci-check.sh
Executable file
17
hack/ci-check.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# If we're doing push build, as opposed to a PR, always run make ci
|
||||
if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then
|
||||
make ci
|
||||
# Exit script early, returning make ci's error
|
||||
exit $?
|
||||
fi
|
||||
|
||||
# Only run `make ci` if files outside of the site directory changed in the branch
|
||||
# In a PR build, $TRAVIS_BRANCH is the destination branch.
|
||||
if [[ $(git diff --name-only $TRAVIS_BRANCH | grep --invert-match site/) ]]; then
|
||||
make ci
|
||||
else
|
||||
echo "Skipping make ci since nothing outside of site directory changed."
|
||||
exit 0
|
||||
fi
|
||||
108
hack/gen-docs.sh
Executable file
108
hack/gen-docs.sh
Executable file
@@ -0,0 +1,108 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 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.
|
||||
|
||||
# gen-docs.sh is used for the "make gen-docs" target. See additional
|
||||
# documentation in the Makefile.
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
# don't run if there's already a directory for the target docs version
|
||||
if [[ -d site/docs/$NEW_DOCS_VERSION ]]; then
|
||||
echo "ERROR: site/docs/$NEW_DOCS_VERSION already exists"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# get the alphabetically last item in site/docs to use as PREVIOUS_DOCS_VERSION
|
||||
# if not explicitly specified by the user
|
||||
if [[ -z "${PREVIOUS_DOCS_VERSION:-}" ]]; then
|
||||
echo "PREVIOUS_DOCS_VERSION was not specified, getting the latest version"
|
||||
PREVIOUS_DOCS_VERSION=$(ls -1 site/docs/ | tail -n 1)
|
||||
fi
|
||||
|
||||
# make a copy of the previous versioned docs dir
|
||||
echo "Creating copy of docs directory site/docs/$PREVIOUS_DOCS_VERSION in site/docs/$NEW_DOCS_VERSION"
|
||||
cp -r site/docs/${PREVIOUS_DOCS_VERSION}/ site/docs/${NEW_DOCS_VERSION}/
|
||||
|
||||
# 'git add' the previous version's docs as-is so we get a useful diff when we copy the master docs in
|
||||
echo "Running 'git add' for previous version's doc contents to use as a base for diff"
|
||||
git add site/docs/${NEW_DOCS_VERSION}
|
||||
|
||||
# now copy the contents of site/docs/master into the same directory so we can get a nice
|
||||
# git diff of what changed since previous version
|
||||
echo "Copying site/docs/master/ to site/docs/${NEW_DOCS_VERSION}/"
|
||||
rm -rf site/docs/${NEW_DOCS_VERSION}/ && cp -r site/docs/master/ site/docs/${NEW_DOCS_VERSION}/
|
||||
|
||||
# make a copy of the previous versioned ToC
|
||||
NEW_DOCS_TOC="$(echo ${NEW_DOCS_VERSION} | tr . -)-toc"
|
||||
PREVIOUS_DOCS_TOC="$(echo ${PREVIOUS_DOCS_VERSION} | tr . -)-toc"
|
||||
|
||||
echo "Creating copy of site/_data/$PREVIOUS_DOCS_TOC.yml at site/_data/$NEW_DOCS_TOC.yml"
|
||||
cp site/_data/$PREVIOUS_DOCS_TOC.yml site/_data/$NEW_DOCS_TOC.yml
|
||||
|
||||
# 'git add' the previous version's ToC content as-is so we get a useful diff when we copy the master ToC in
|
||||
echo "Running 'git add' for previous version's ToC to use as a base for diff"
|
||||
git add site/_data/$NEW_DOCS_TOC.yml
|
||||
|
||||
# now copy the master ToC so we can get a nice git diff of what changed since previous version
|
||||
echo "Copying site/_data/master-toc.yml to site/_data/$NEW_DOCS_TOC.yml"
|
||||
rm site/_data/$NEW_DOCS_TOC.yml && cp site/_data/master-toc.yml site/_data/$NEW_DOCS_TOC.yml
|
||||
|
||||
# replace known version-specific links -- the sed syntax is slightly different in OS X and Linux,
|
||||
# so check which OS we're running on.
|
||||
if [[ $(uname) == "Darwin" ]]; then
|
||||
echo "[OS X] updating version-specific links"
|
||||
find site/docs/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i '' "s|https://velero.io/docs/master|https://velero.io/docs/$NEW_DOCS_VERSION|g"
|
||||
find site/docs/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i '' "s|https://github.com/heptio/velero/blob/master|https://github.com/heptio/velero/blob/$NEW_DOCS_VERSION|g"
|
||||
|
||||
echo "[OS X] Updating latest version in _config.yml"
|
||||
sed -i '' "s/latest: ${PREVIOUS_DOCS_VERSION}/latest: ${NEW_DOCS_VERSION}/" site/_config.yml
|
||||
|
||||
# newlines and lack of indentation are requirements for this sed syntax
|
||||
# which is doing an append
|
||||
echo "[OS X] Adding latest version to versions list in _config.yml"
|
||||
sed -i '' "/- master/a\\
|
||||
- ${NEW_DOCS_VERSION}
|
||||
" site/_config.yml
|
||||
|
||||
echo "[OS X] Adding ToC mapping entry"
|
||||
sed -i '' "/master: master-toc/a\\
|
||||
${NEW_DOCS_VERSION}: ${NEW_DOCS_TOC}
|
||||
" site/_data/toc-mapping.yml
|
||||
|
||||
else
|
||||
echo "[Linux] updating version-specific links"
|
||||
find site/docs/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i'' "s|https://velero.io/docs/master|https://velero.io/docs/$NEW_DOCS_VERSION|g"
|
||||
find site/docs/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i'' "s|https://github.com/heptio/velero/blob/master|https://github.com/heptio/velero/blob/$NEW_DOCS_VERSION|g"
|
||||
|
||||
echo "[Linux] Updating latest version in _config.yml"
|
||||
sed -i'' "s/latest: ${PREVIOUS_DOCS_VERSION}/latest: ${NEW_DOCS_VERSION}/" site/_config.yml
|
||||
|
||||
echo "[Linux] Adding latest version to versions list in _config.yml"
|
||||
sed -i'' "/- master/a - ${NEW_DOCS_VERSION}" site/_config.yml
|
||||
|
||||
echo "[Linux] Adding ToC mapping entry"
|
||||
sed -i'' "/master: master-toc/a ${NEW_DOCS_VERSION}: ${NEW_DOCS_TOC}" site/_data/toc-mapping.yml
|
||||
fi
|
||||
|
||||
echo "Success! site/docs/$NEW_DOCS_VERSION has been created."
|
||||
echo ""
|
||||
echo "The next steps are:"
|
||||
echo " 1. Consult site/README-JEKYLL.md for further manual steps required to finalize the new versioned docs generation."
|
||||
echo " 2. Run a 'git diff' to review all changes made to the docs since the previous version."
|
||||
echo " 3. Make any manual changes/corrections necessary."
|
||||
echo " 4. Run 'git add' to stage all unstaged changes, then 'git commit'."
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash -e
|
||||
#
|
||||
# Copyright 2018 the Velero contributors.
|
||||
# Copyright 2018, 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.
|
||||
@@ -20,7 +20,7 @@ BIN=${VELERO_ROOT}/_output/bin
|
||||
mkdir -p ${BIN}
|
||||
|
||||
echo "Updating generated Github issue template"
|
||||
go build -o ${BIN}/issue-tmpl-gen ./docs/issue-template-gen/main.go
|
||||
go build -o ${BIN}/issue-tmpl-gen ./hack/issue-template-gen/main.go
|
||||
|
||||
if [[ $# -gt 1 ]]; then
|
||||
echo "usage: ${BASH_SOURCE} [OUTPUT_FILE]"
|
||||
|
||||
@@ -66,6 +66,9 @@ type BackupStorageLocationSpec struct {
|
||||
Config map[string]string `json:"config"`
|
||||
|
||||
StorageType `json:",inline"`
|
||||
|
||||
// AccessMode defines the permissions for the backup storage location.
|
||||
AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"`
|
||||
}
|
||||
|
||||
// BackupStorageLocationPhase is the lifecyle phase of a Velero BackupStorageLocation.
|
||||
@@ -90,10 +93,17 @@ const (
|
||||
BackupStorageLocationAccessModeReadWrite BackupStorageLocationAccessMode = "ReadWrite"
|
||||
)
|
||||
|
||||
// TODO(2.0): remove the AccessMode field from BackupStorageLocationStatus.
|
||||
|
||||
// BackupStorageLocationStatus describes the current status of a Velero BackupStorageLocation.
|
||||
type BackupStorageLocationStatus struct {
|
||||
Phase BackupStorageLocationPhase `json:"phase,omitempty"`
|
||||
AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"`
|
||||
LastSyncedRevision types.UID `json:"lastSyncedRevision,omitempty"`
|
||||
LastSyncedTime metav1.Time `json:"lastSyncedTime,omitempty"`
|
||||
Phase BackupStorageLocationPhase `json:"phase,omitempty"`
|
||||
LastSyncedRevision types.UID `json:"lastSyncedRevision,omitempty"`
|
||||
LastSyncedTime metav1.Time `json:"lastSyncedTime,omitempty"`
|
||||
|
||||
// AccessMode is an unused field.
|
||||
//
|
||||
// Deprecated: there is now an AccessMode field on the Spec and this field
|
||||
// will be removed entirely as of v2.0.
|
||||
AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"`
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ const (
|
||||
DownloadTargetKindBackupLog DownloadTargetKind = "BackupLog"
|
||||
DownloadTargetKindBackupContents DownloadTargetKind = "BackupContents"
|
||||
DownloadTargetKindBackupVolumeSnapshots DownloadTargetKind = "BackupVolumeSnapshots"
|
||||
DownloadTargetKindBackupResourceList DownloadTargetKind = "BackupResourceList"
|
||||
DownloadTargetKindRestoreLog DownloadTargetKind = "RestoreLog"
|
||||
DownloadTargetKindRestoreResults DownloadTargetKind = "RestoreResults"
|
||||
)
|
||||
|
||||
@@ -68,6 +68,18 @@ type PodVolumeBackupStatus struct {
|
||||
|
||||
// Message is a message about the pod volume backup's status.
|
||||
Message string `json:"message"`
|
||||
|
||||
// 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
|
||||
StartTimestamp metav1.Time `json:"startTimestamp"`
|
||||
|
||||
// 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
|
||||
CompletionTimestamp metav1.Time `json:"completionTimestamp"`
|
||||
}
|
||||
|
||||
// +genclient
|
||||
|
||||
@@ -57,6 +57,15 @@ type PodVolumeRestoreStatus struct {
|
||||
|
||||
// Message is a message about the pod volume restore's status.
|
||||
Message string `json:"message"`
|
||||
|
||||
// StartTimestamp records the time a restore was started.
|
||||
// The server's time is used for StartTimestamps
|
||||
StartTimestamp metav1.Time `json:"startTimestamp"`
|
||||
|
||||
// CompletionTimestamp records the time a restore was completed.
|
||||
// Completion time is recorded even on failed restores.
|
||||
// The server's time is used for CompletionTimestamps
|
||||
CompletionTimestamp metav1.Time `json:"completionTimestamp"`
|
||||
}
|
||||
|
||||
// +genclient
|
||||
|
||||
@@ -16,7 +16,9 @@ limitations under the License.
|
||||
|
||||
package v1
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
@@ -45,6 +47,12 @@ const (
|
||||
ServerStatusRequestPhaseProcessed ServerStatusRequestPhase = "Processed"
|
||||
)
|
||||
|
||||
// PluginInfo contains attributes of a Velero plugin
|
||||
type PluginInfo struct {
|
||||
Name string `json:"name"`
|
||||
Kind string `json:"kind"`
|
||||
}
|
||||
|
||||
// ServerStatusRequestStatus is the current status of a ServerStatusRequest.
|
||||
type ServerStatusRequestStatus struct {
|
||||
// Phase is the current lifecycle phase of the ServerStatusRequest.
|
||||
@@ -56,6 +64,9 @@ type ServerStatusRequestStatus struct {
|
||||
|
||||
// ServerVersion is the Velero server version.
|
||||
ServerVersion string `json:"serverVersion"`
|
||||
|
||||
// Plugins list information about the plugins running on the Velero server
|
||||
Plugins []PluginInfo `json:"plugins"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
@@ -616,13 +616,29 @@ func (in *ObjectStorageLocation) DeepCopy() *ObjectStorageLocation {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PluginInfo) DeepCopyInto(out *PluginInfo) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PluginInfo.
|
||||
func (in *PluginInfo) DeepCopy() *PluginInfo {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PluginInfo)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodVolumeBackup) DeepCopyInto(out *PodVolumeBackup) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -704,6 +720,8 @@ func (in *PodVolumeBackupSpec) DeepCopy() *PodVolumeBackupSpec {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodVolumeBackupStatus) DeepCopyInto(out *PodVolumeBackupStatus) {
|
||||
*out = *in
|
||||
in.StartTimestamp.DeepCopyInto(&out.StartTimestamp)
|
||||
in.CompletionTimestamp.DeepCopyInto(&out.CompletionTimestamp)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -723,7 +741,7 @@ func (in *PodVolumeRestore) DeepCopyInto(out *PodVolumeRestore) {
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
out.Status = in.Status
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -798,6 +816,8 @@ func (in *PodVolumeRestoreSpec) DeepCopy() *PodVolumeRestoreSpec {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodVolumeRestoreStatus) DeepCopyInto(out *PodVolumeRestoreStatus) {
|
||||
*out = *in
|
||||
in.StartTimestamp.DeepCopyInto(&out.StartTimestamp)
|
||||
in.CompletionTimestamp.DeepCopyInto(&out.CompletionTimestamp)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1227,6 +1247,11 @@ func (in *ServerStatusRequestSpec) DeepCopy() *ServerStatusRequestSpec {
|
||||
func (in *ServerStatusRequestStatus) DeepCopyInto(out *ServerStatusRequestStatus) {
|
||||
*out = *in
|
||||
in.ProcessedTimestamp.DeepCopyInto(&out.ProcessedTimestamp)
|
||||
if in.Plugins != nil {
|
||||
in, out := &in.Plugins, &out.Plugins
|
||||
*out = make([]PluginInfo, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -60,12 +60,6 @@ type kubernetesBackupper struct {
|
||||
resticTimeout time.Duration
|
||||
}
|
||||
|
||||
type itemKey struct {
|
||||
resource string
|
||||
namespace string
|
||||
name string
|
||||
}
|
||||
|
||||
type resolvedAction struct {
|
||||
velero.BackupItemAction
|
||||
|
||||
@@ -240,6 +234,8 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req
|
||||
return err
|
||||
}
|
||||
|
||||
backupRequest.BackedUpItems = map[itemKey]struct{}{}
|
||||
|
||||
podVolumeTimeout := kb.resticTimeout
|
||||
if val := backupRequest.Annotations[api.PodVolumeOperationTimeoutAnnotation]; val != "" {
|
||||
parsed, err := time.ParseDuration(val)
|
||||
@@ -266,7 +262,6 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req
|
||||
backupRequest,
|
||||
kb.dynamicFactory,
|
||||
kb.discoveryHelper,
|
||||
make(map[itemKey]struct{}),
|
||||
cohabitatingResources(),
|
||||
kb.podCommandExecutor,
|
||||
tw,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -37,7 +37,6 @@ type groupBackupperFactory interface {
|
||||
backupRequest *Request,
|
||||
dynamicFactory client.DynamicFactory,
|
||||
discoveryHelper discovery.Helper,
|
||||
backedUpItems map[itemKey]struct{},
|
||||
cohabitatingResources map[string]*cohabitatingResource,
|
||||
podCommandExecutor podexec.PodCommandExecutor,
|
||||
tarWriter tarWriter,
|
||||
@@ -54,7 +53,6 @@ func (f *defaultGroupBackupperFactory) newGroupBackupper(
|
||||
backupRequest *Request,
|
||||
dynamicFactory client.DynamicFactory,
|
||||
discoveryHelper discovery.Helper,
|
||||
backedUpItems map[itemKey]struct{},
|
||||
cohabitatingResources map[string]*cohabitatingResource,
|
||||
podCommandExecutor podexec.PodCommandExecutor,
|
||||
tarWriter tarWriter,
|
||||
@@ -67,7 +65,6 @@ func (f *defaultGroupBackupperFactory) newGroupBackupper(
|
||||
backupRequest: backupRequest,
|
||||
dynamicFactory: dynamicFactory,
|
||||
discoveryHelper: discoveryHelper,
|
||||
backedUpItems: backedUpItems,
|
||||
cohabitatingResources: cohabitatingResources,
|
||||
podCommandExecutor: podCommandExecutor,
|
||||
tarWriter: tarWriter,
|
||||
@@ -88,7 +85,6 @@ type defaultGroupBackupper struct {
|
||||
backupRequest *Request
|
||||
dynamicFactory client.DynamicFactory
|
||||
discoveryHelper discovery.Helper
|
||||
backedUpItems map[itemKey]struct{}
|
||||
cohabitatingResources map[string]*cohabitatingResource
|
||||
podCommandExecutor podexec.PodCommandExecutor
|
||||
tarWriter tarWriter
|
||||
@@ -120,7 +116,6 @@ func (gb *defaultGroupBackupper) backupGroup(group *metav1.APIResourceList) erro
|
||||
gb.backupRequest,
|
||||
gb.dynamicFactory,
|
||||
gb.discoveryHelper,
|
||||
gb.backedUpItems,
|
||||
gb.cohabitatingResources,
|
||||
gb.podCommandExecutor,
|
||||
gb.tarWriter,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 the Velero contributors.
|
||||
Copyright 2017, 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.
|
||||
@@ -19,111 +19,10 @@ package backup
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/heptio/velero/pkg/client"
|
||||
"github.com/heptio/velero/pkg/discovery"
|
||||
"github.com/heptio/velero/pkg/podexec"
|
||||
"github.com/heptio/velero/pkg/restic"
|
||||
velerotest "github.com/heptio/velero/pkg/util/test"
|
||||
)
|
||||
|
||||
func TestBackupGroupBacksUpCorrectResourcesInCorrectOrder(t *testing.T) {
|
||||
resourceBackupperFactory := new(mockResourceBackupperFactory)
|
||||
resourceBackupper := new(mockResourceBackupper)
|
||||
|
||||
defer resourceBackupperFactory.AssertExpectations(t)
|
||||
defer resourceBackupper.AssertExpectations(t)
|
||||
|
||||
resourceBackupperFactory.On("newResourceBackupper",
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
).Return(resourceBackupper)
|
||||
|
||||
gb := &defaultGroupBackupper{
|
||||
log: velerotest.NewLogger(),
|
||||
resourceBackupperFactory: resourceBackupperFactory,
|
||||
}
|
||||
|
||||
group := &metav1.APIResourceList{
|
||||
GroupVersion: "v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "persistentvolumes"},
|
||||
{Name: "pods"},
|
||||
{Name: "persistentvolumeclaims"},
|
||||
},
|
||||
}
|
||||
|
||||
var actualOrder []string
|
||||
runFunc := func(args mock.Arguments) {
|
||||
actualOrder = append(actualOrder, args.Get(1).(metav1.APIResource).Name)
|
||||
}
|
||||
|
||||
resourceBackupper.On("backupResource", group, metav1.APIResource{Name: "pods"}).Return(nil).Run(runFunc)
|
||||
resourceBackupper.On("backupResource", group, metav1.APIResource{Name: "persistentvolumeclaims"}).Return(nil).Run(runFunc)
|
||||
resourceBackupper.On("backupResource", group, metav1.APIResource{Name: "persistentvolumes"}).Return(nil).Run(runFunc)
|
||||
|
||||
require.NoError(t, gb.backupGroup(group))
|
||||
|
||||
// make sure PVs were last
|
||||
assert.Equal(t, []string{"pods", "persistentvolumeclaims", "persistentvolumes"}, actualOrder)
|
||||
}
|
||||
|
||||
type mockResourceBackupperFactory struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (rbf *mockResourceBackupperFactory) newResourceBackupper(
|
||||
log logrus.FieldLogger,
|
||||
backup *Request,
|
||||
dynamicFactory client.DynamicFactory,
|
||||
discoveryHelper discovery.Helper,
|
||||
backedUpItems map[itemKey]struct{},
|
||||
cohabitatingResources map[string]*cohabitatingResource,
|
||||
podCommandExecutor podexec.PodCommandExecutor,
|
||||
tarWriter tarWriter,
|
||||
resticBackupper restic.Backupper,
|
||||
resticSnapshotTracker *pvcSnapshotTracker,
|
||||
volumeSnapshotterGetter VolumeSnapshotterGetter,
|
||||
) resourceBackupper {
|
||||
args := rbf.Called(
|
||||
log,
|
||||
backup,
|
||||
dynamicFactory,
|
||||
discoveryHelper,
|
||||
backedUpItems,
|
||||
cohabitatingResources,
|
||||
podCommandExecutor,
|
||||
tarWriter,
|
||||
resticBackupper,
|
||||
resticSnapshotTracker,
|
||||
volumeSnapshotterGetter,
|
||||
)
|
||||
return args.Get(0).(resourceBackupper)
|
||||
}
|
||||
|
||||
type mockResourceBackupper struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (rb *mockResourceBackupper) backupResource(group *metav1.APIResourceList, resource metav1.APIResource) error {
|
||||
args := rb.Called(group, resource)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func TestSortCoreGroup(t *testing.T) {
|
||||
group := &metav1.APIResourceList{
|
||||
GroupVersion: "v1",
|
||||
|
||||
@@ -19,6 +19,7 @@ package backup
|
||||
import (
|
||||
"archive/tar"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
@@ -33,6 +34,7 @@ import (
|
||||
kubeerrs "k8s.io/apimachinery/pkg/util/errors"
|
||||
|
||||
api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
"github.com/heptio/velero/pkg/client"
|
||||
"github.com/heptio/velero/pkg/discovery"
|
||||
"github.com/heptio/velero/pkg/kuberesource"
|
||||
@@ -45,7 +47,6 @@ import (
|
||||
type itemBackupperFactory interface {
|
||||
newItemBackupper(
|
||||
backup *Request,
|
||||
backedUpItems map[itemKey]struct{},
|
||||
podCommandExecutor podexec.PodCommandExecutor,
|
||||
tarWriter tarWriter,
|
||||
dynamicFactory client.DynamicFactory,
|
||||
@@ -60,7 +61,6 @@ type defaultItemBackupperFactory struct{}
|
||||
|
||||
func (f *defaultItemBackupperFactory) newItemBackupper(
|
||||
backupRequest *Request,
|
||||
backedUpItems map[itemKey]struct{},
|
||||
podCommandExecutor podexec.PodCommandExecutor,
|
||||
tarWriter tarWriter,
|
||||
dynamicFactory client.DynamicFactory,
|
||||
@@ -71,7 +71,6 @@ func (f *defaultItemBackupperFactory) newItemBackupper(
|
||||
) ItemBackupper {
|
||||
ib := &defaultItemBackupper{
|
||||
backupRequest: backupRequest,
|
||||
backedUpItems: backedUpItems,
|
||||
tarWriter: tarWriter,
|
||||
dynamicFactory: dynamicFactory,
|
||||
discoveryHelper: discoveryHelper,
|
||||
@@ -96,7 +95,6 @@ type ItemBackupper interface {
|
||||
|
||||
type defaultItemBackupper struct {
|
||||
backupRequest *Request
|
||||
backedUpItems map[itemKey]struct{}
|
||||
tarWriter tarWriter
|
||||
dynamicFactory client.DynamicFactory
|
||||
discoveryHelper discovery.Helper
|
||||
@@ -148,17 +146,18 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim
|
||||
log.Info("Skipping item because it's being deleted.")
|
||||
return nil
|
||||
}
|
||||
|
||||
key := itemKey{
|
||||
resource: groupResource.String(),
|
||||
resource: resourceKey(obj),
|
||||
namespace: namespace,
|
||||
name: name,
|
||||
}
|
||||
|
||||
if _, exists := ib.backedUpItems[key]; exists {
|
||||
if _, exists := ib.backupRequest.BackedUpItems[key]; exists {
|
||||
log.Info("Skipping item because it's already been backed up.")
|
||||
return nil
|
||||
}
|
||||
ib.backedUpItems[key] = struct{}{}
|
||||
ib.backupRequest.BackedUpItems[key] = struct{}{}
|
||||
|
||||
log.Info("Backing up item")
|
||||
|
||||
@@ -206,6 +205,9 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim
|
||||
if metadata, err = meta.Accessor(obj); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
// update name and namespace in case they were modified in an action
|
||||
name = metadata.GetName()
|
||||
namespace = metadata.GetNamespace()
|
||||
|
||||
if groupResource == kuberesource.PersistentVolumes {
|
||||
if err := ib.takePVSnapshot(obj, log); err != nil {
|
||||
@@ -214,15 +216,10 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim
|
||||
}
|
||||
|
||||
if groupResource == kuberesource.Pods && pod != nil {
|
||||
// this function will return partial results, so process volumeSnapshots
|
||||
// this function will return partial results, so process podVolumeBackups
|
||||
// even if there are errors.
|
||||
volumeSnapshots, errs := ib.backupPodVolumes(log, pod, resticVolumesToBackup)
|
||||
|
||||
// annotate the pod with the successful volume snapshots
|
||||
for volume, snapshot := range volumeSnapshots {
|
||||
restic.SetPodSnapshotAnnotation(metadata, volume, snapshot)
|
||||
}
|
||||
|
||||
podVolumeBackups, errs := ib.backupPodVolumes(log, pod, resticVolumesToBackup)
|
||||
ib.backupRequest.PodVolumeBackups = podVolumeBackups
|
||||
backupErrs = append(backupErrs, errs...)
|
||||
}
|
||||
|
||||
@@ -266,9 +263,9 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim
|
||||
return nil
|
||||
}
|
||||
|
||||
// backupPodVolumes triggers restic backups of the specified pod volumes, and returns a map of volume name -> snapshot ID
|
||||
// backupPodVolumes triggers restic backups of the specified pod volumes, and returns a list of PodVolumeBackups
|
||||
// for volumes that were successfully backed up, and a slice of any errors that were encountered.
|
||||
func (ib *defaultItemBackupper) backupPodVolumes(log logrus.FieldLogger, pod *corev1api.Pod, volumes []string) (map[string]string, []error) {
|
||||
func (ib *defaultItemBackupper) backupPodVolumes(log logrus.FieldLogger, pod *corev1api.Pod, volumes []string) ([]*velerov1api.PodVolumeBackup, []error) {
|
||||
if len(volumes) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -299,6 +296,11 @@ func (ib *defaultItemBackupper) executeActions(
|
||||
continue
|
||||
}
|
||||
|
||||
if namespace == "" && !action.namespaceIncludesExcludes.IncludeEverything() {
|
||||
log.Debug("Skipping action because resource is cluster-scoped and action only applies to specific namespaces")
|
||||
continue
|
||||
}
|
||||
|
||||
if !action.selector.Matches(labels.Set(metadata.GetLabels())) {
|
||||
log.Debug("Skipping action because label selector does not match")
|
||||
continue
|
||||
@@ -433,10 +435,12 @@ func (ib *defaultItemBackupper) takePVSnapshot(obj runtime.Unstructured, log log
|
||||
|
||||
log = log.WithField("volumeID", volumeID)
|
||||
|
||||
tags := map[string]string{
|
||||
"velero.io/backup": ib.backupRequest.Name,
|
||||
"velero.io/pv": pv.Name,
|
||||
tags := ib.backupRequest.GetLabels()
|
||||
if tags == nil {
|
||||
tags = map[string]string{}
|
||||
}
|
||||
tags["velero.io/backup"] = ib.backupRequest.Name
|
||||
tags["velero.io/pv"] = pv.Name
|
||||
|
||||
log.Info("Getting volume information")
|
||||
volumeType, iops, err := volumeSnapshotter.GetVolumeInfo(volumeID, pvFailureDomainZone)
|
||||
@@ -479,3 +483,10 @@ func volumeSnapshot(backup *api.Backup, volumeName, volumeID, volumeType, az, lo
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// resourceKey returns a string representing the object's GroupVersionKind (e.g.
|
||||
// apps/v1/Deployment).
|
||||
func resourceKey(obj runtime.Unstructured) string {
|
||||
gvk := obj.GetObjectKind().GroupVersionKind()
|
||||
return fmt.Sprintf("%s/%s", gvk.GroupVersion().String(), gvk.Kind)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017, 2019 the Velero contributors.
|
||||
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.
|
||||
@@ -17,853 +17,31 @@ limitations under the License.
|
||||
package backup
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
v1 "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
resticmocks "github.com/heptio/velero/pkg/restic/mocks"
|
||||
"github.com/heptio/velero/pkg/util/collections"
|
||||
velerotest "github.com/heptio/velero/pkg/util/test"
|
||||
"github.com/heptio/velero/pkg/builder"
|
||||
)
|
||||
|
||||
func TestBackupItemSkips(t *testing.T) {
|
||||
func Test_resourceKey(t *testing.T) {
|
||||
tests := []struct {
|
||||
testName string
|
||||
namespace string
|
||||
name string
|
||||
namespaces *collections.IncludesExcludes
|
||||
groupResource schema.GroupResource
|
||||
resources *collections.IncludesExcludes
|
||||
terminating bool
|
||||
backedUpItems map[itemKey]struct{}
|
||||
resource metav1.Object
|
||||
want string
|
||||
}{
|
||||
{
|
||||
testName: "namespace not in includes list",
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
namespaces: collections.NewIncludesExcludes().Includes("a"),
|
||||
},
|
||||
{
|
||||
testName: "namespace in excludes list",
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
namespaces: collections.NewIncludesExcludes().Excludes("ns"),
|
||||
},
|
||||
{
|
||||
testName: "resource not in includes list",
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
groupResource: schema.GroupResource{Group: "foo", Resource: "bar"},
|
||||
namespaces: collections.NewIncludesExcludes(),
|
||||
resources: collections.NewIncludesExcludes().Includes("a.b"),
|
||||
},
|
||||
{
|
||||
testName: "resource in excludes list",
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
groupResource: schema.GroupResource{Group: "foo", Resource: "bar"},
|
||||
namespaces: collections.NewIncludesExcludes(),
|
||||
resources: collections.NewIncludesExcludes().Excludes("bar.foo"),
|
||||
},
|
||||
{
|
||||
testName: "resource already backed up",
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
groupResource: schema.GroupResource{Group: "foo", Resource: "bar"},
|
||||
namespaces: collections.NewIncludesExcludes(),
|
||||
resources: collections.NewIncludesExcludes(),
|
||||
backedUpItems: map[itemKey]struct{}{
|
||||
{resource: "bar.foo", namespace: "ns", name: "foo"}: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "terminating resource",
|
||||
namespace: "ns",
|
||||
name: "foo",
|
||||
groupResource: schema.GroupResource{Group: "foo", Resource: "bar"},
|
||||
namespaces: collections.NewIncludesExcludes(),
|
||||
resources: collections.NewIncludesExcludes(),
|
||||
terminating: true,
|
||||
},
|
||||
{resource: builder.ForPod("default", "test").Result(), want: "v1/Pod"},
|
||||
{resource: builder.ForDeployment("default", "test").Result(), want: "apps/v1/Deployment"},
|
||||
{resource: builder.ForPersistentVolume("test").Result(), want: "v1/PersistentVolume"},
|
||||
{resource: builder.ForRole("default", "test").Result(), want: "rbac.authorization.k8s.io/v1/Role"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.testName, func(t *testing.T) {
|
||||
req := &Request{
|
||||
NamespaceIncludesExcludes: test.namespaces,
|
||||
ResourceIncludesExcludes: test.resources,
|
||||
}
|
||||
|
||||
ib := &defaultItemBackupper{
|
||||
backupRequest: req,
|
||||
backedUpItems: test.backedUpItems,
|
||||
}
|
||||
|
||||
pod := &corev1api.Pod{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Pod"},
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: test.namespace, Name: test.name},
|
||||
}
|
||||
|
||||
if test.terminating {
|
||||
pod.ObjectMeta.DeletionTimestamp = &metav1.Time{Time: time.Now()}
|
||||
}
|
||||
unstructuredObj, unmarshalErr := runtime.DefaultUnstructuredConverter.ToUnstructured(pod)
|
||||
require.NoError(t, unmarshalErr)
|
||||
u := &unstructured.Unstructured{Object: unstructuredObj}
|
||||
err := ib.backupItem(velerotest.NewLogger(), u, test.groupResource)
|
||||
assert.NoError(t, err)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.want, func(t *testing.T) {
|
||||
content, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(tt.resource)
|
||||
unstructured := &unstructured.Unstructured{Object: content}
|
||||
assert.Equal(t, tt.want, resourceKey(unstructured))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackupItemSkipsClusterScopedResourceWhenIncludeClusterResourcesFalse(t *testing.T) {
|
||||
f := false
|
||||
ib := &defaultItemBackupper{
|
||||
backupRequest: &Request{
|
||||
Backup: &v1.Backup{
|
||||
Spec: v1.BackupSpec{
|
||||
IncludeClusterResources: &f,
|
||||
},
|
||||
},
|
||||
NamespaceIncludesExcludes: collections.NewIncludesExcludes(),
|
||||
ResourceIncludesExcludes: collections.NewIncludesExcludes(),
|
||||
},
|
||||
}
|
||||
|
||||
u := velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Foo","metadata":{"name":"bar"}}`)
|
||||
err := ib.backupItem(velerotest.NewLogger(), u, schema.GroupResource{Group: "foo", Resource: "bar"})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestBackupItemNoSkips(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
item string
|
||||
namespaceIncludesExcludes *collections.IncludesExcludes
|
||||
expectError bool
|
||||
expectExcluded bool
|
||||
expectedTarHeaderName string
|
||||
tarWriteError bool
|
||||
tarHeaderWriteError bool
|
||||
customAction bool
|
||||
expectedActionID string
|
||||
customActionAdditionalItemIdentifiers []velero.ResourceIdentifier
|
||||
customActionAdditionalItems []runtime.Unstructured
|
||||
groupResource string
|
||||
snapshottableVolumes map[string]velerotest.VolumeBackupInfo
|
||||
snapshotError error
|
||||
additionalItemError error
|
||||
trackedPVCs sets.String
|
||||
expectedTrackedPVCs sets.String
|
||||
}{
|
||||
{
|
||||
name: "explicit namespace include",
|
||||
item: `{"metadata":{"namespace":"foo","name":"bar"}}`,
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("foo"),
|
||||
expectError: false,
|
||||
expectExcluded: false,
|
||||
expectedTarHeaderName: "resources/resource.group/namespaces/foo/bar.json",
|
||||
},
|
||||
{
|
||||
name: "* namespace include",
|
||||
item: `{"metadata":{"namespace":"foo","name":"bar"}}`,
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
expectError: false,
|
||||
expectExcluded: false,
|
||||
expectedTarHeaderName: "resources/resource.group/namespaces/foo/bar.json",
|
||||
},
|
||||
{
|
||||
name: "cluster-scoped",
|
||||
item: `{"metadata":{"name":"bar"}}`,
|
||||
expectError: false,
|
||||
expectExcluded: false,
|
||||
expectedTarHeaderName: "resources/resource.group/cluster/bar.json",
|
||||
},
|
||||
{
|
||||
name: "tar header write error",
|
||||
item: `{"metadata":{"name":"bar"},"spec":{"color":"green"},"status":{"foo":"bar"}}`,
|
||||
expectError: true,
|
||||
tarHeaderWriteError: true,
|
||||
},
|
||||
{
|
||||
name: "tar write error",
|
||||
item: `{"metadata":{"name":"bar"},"spec":{"color":"green"},"status":{"foo":"bar"}}`,
|
||||
expectError: true,
|
||||
tarWriteError: true,
|
||||
},
|
||||
{
|
||||
name: "action invoked - cluster-scoped",
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
item: `{"metadata":{"name":"bar"}}`,
|
||||
expectError: false,
|
||||
expectExcluded: false,
|
||||
expectedTarHeaderName: "resources/resource.group/cluster/bar.json",
|
||||
customAction: true,
|
||||
expectedActionID: "bar",
|
||||
},
|
||||
{
|
||||
name: "action invoked - namespaced",
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
item: `{"metadata":{"namespace": "myns", "name":"bar"}}`,
|
||||
expectError: false,
|
||||
expectExcluded: false,
|
||||
expectedTarHeaderName: "resources/resource.group/namespaces/myns/bar.json",
|
||||
customAction: true,
|
||||
expectedActionID: "myns/bar",
|
||||
},
|
||||
{
|
||||
name: "action invoked - additional items",
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
item: `{"metadata":{"namespace": "myns", "name":"bar"}}`,
|
||||
expectError: false,
|
||||
expectExcluded: false,
|
||||
expectedTarHeaderName: "resources/resource.group/namespaces/myns/bar.json",
|
||||
customAction: true,
|
||||
expectedActionID: "myns/bar",
|
||||
customActionAdditionalItemIdentifiers: []velero.ResourceIdentifier{
|
||||
{
|
||||
GroupResource: schema.GroupResource{Group: "g1", Resource: "r1"},
|
||||
Namespace: "ns1",
|
||||
Name: "n1",
|
||||
},
|
||||
{
|
||||
GroupResource: schema.GroupResource{Group: "g2", Resource: "r2"},
|
||||
Namespace: "ns2",
|
||||
Name: "n2",
|
||||
},
|
||||
},
|
||||
customActionAdditionalItems: []runtime.Unstructured{
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"g1/v1","kind":"r1","metadata":{"namespace":"ns1","name":"n1"}}`),
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"g2/v1","kind":"r1","metadata":{"namespace":"ns2","name":"n2"}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "action invoked - additional items - error",
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
item: `{"metadata":{"namespace": "myns", "name":"bar"}}`,
|
||||
expectError: true,
|
||||
expectExcluded: false,
|
||||
expectedTarHeaderName: "resources/resource.group/namespaces/myns/bar.json",
|
||||
customAction: true,
|
||||
expectedActionID: "myns/bar",
|
||||
customActionAdditionalItemIdentifiers: []velero.ResourceIdentifier{
|
||||
{
|
||||
GroupResource: schema.GroupResource{Group: "g1", Resource: "r1"},
|
||||
Namespace: "ns1",
|
||||
Name: "n1",
|
||||
},
|
||||
{
|
||||
GroupResource: schema.GroupResource{Group: "g2", Resource: "r2"},
|
||||
Namespace: "ns2",
|
||||
Name: "n2",
|
||||
},
|
||||
},
|
||||
customActionAdditionalItems: []runtime.Unstructured{
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"g1/v1","kind":"r1","metadata":{"namespace":"ns1","name":"n1"}}`),
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"g2/v1","kind":"r1","metadata":{"namespace":"ns2","name":"n2"}}`),
|
||||
},
|
||||
additionalItemError: errors.New("foo"),
|
||||
},
|
||||
{
|
||||
name: "takePVSnapshot is not invoked for PVs when volumeSnapshotter == nil",
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
item: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
|
||||
expectError: false,
|
||||
expectExcluded: false,
|
||||
expectedTarHeaderName: "resources/persistentvolumes/cluster/mypv.json",
|
||||
groupResource: "persistentvolumes",
|
||||
},
|
||||
{
|
||||
name: "takePVSnapshot is invoked for PVs when volumeSnapshotter != nil",
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
item: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
|
||||
expectError: false,
|
||||
expectExcluded: false,
|
||||
expectedTarHeaderName: "resources/persistentvolumes/cluster/mypv.json",
|
||||
groupResource: "persistentvolumes",
|
||||
snapshottableVolumes: map[string]velerotest.VolumeBackupInfo{
|
||||
"vol-abc123": {SnapshotID: "snapshot-1", AvailabilityZone: "us-east-1c"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "takePVSnapshot is not invoked for PVs when their claim is tracked in the restic PVC tracker",
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
item: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"claimRef": {"namespace": "pvc-ns", "name": "pvc"}, "awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
|
||||
expectError: false,
|
||||
expectExcluded: false,
|
||||
expectedTarHeaderName: "resources/persistentvolumes/cluster/mypv.json",
|
||||
groupResource: "persistentvolumes",
|
||||
// empty snapshottableVolumes causes a volumeSnapshotter to be created, but no
|
||||
// snapshots are expected to be taken.
|
||||
snapshottableVolumes: map[string]velerotest.VolumeBackupInfo{},
|
||||
trackedPVCs: sets.NewString(key("pvc-ns", "pvc"), key("another-pvc-ns", "another-pvc")),
|
||||
},
|
||||
{
|
||||
name: "takePVSnapshot is invoked for PVs when their claim is not tracked in the restic PVC tracker",
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
item: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"claimRef": {"namespace": "pvc-ns", "name": "pvc"}, "awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
|
||||
expectError: false,
|
||||
expectExcluded: false,
|
||||
expectedTarHeaderName: "resources/persistentvolumes/cluster/mypv.json",
|
||||
groupResource: "persistentvolumes",
|
||||
snapshottableVolumes: map[string]velerotest.VolumeBackupInfo{
|
||||
"vol-abc123": {SnapshotID: "snapshot-1", AvailabilityZone: "us-east-1c"},
|
||||
},
|
||||
trackedPVCs: sets.NewString(key("another-pvc-ns", "another-pvc")),
|
||||
},
|
||||
{
|
||||
name: "backup fails when takePVSnapshot fails",
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
item: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
|
||||
expectError: true,
|
||||
groupResource: "persistentvolumes",
|
||||
snapshottableVolumes: map[string]velerotest.VolumeBackupInfo{
|
||||
"vol-abc123": {SnapshotID: "snapshot-1", AvailabilityZone: "us-east-1c"},
|
||||
},
|
||||
snapshotError: fmt.Errorf("failure"),
|
||||
},
|
||||
{
|
||||
name: "pod's restic PVC volume backups (only) are tracked",
|
||||
item: `{"apiVersion": "v1", "kind": "Pod", "spec": {"volumes": [{"name": "volume-1", "persistentVolumeClaim": {"claimName": "bar"}},{"name": "volume-2", "persistentVolumeClaim": {"claimName": "baz"}},{"name": "volume-1", "emptyDir": {}}]}, "metadata":{"namespace":"foo","name":"bar", "annotations": {"backup.velero.io/backup-volumes": "volume-1,volume-2"}}}`,
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
groupResource: "pods",
|
||||
expectError: false,
|
||||
expectExcluded: false,
|
||||
expectedTarHeaderName: "resources/pods/namespaces/foo/bar.json",
|
||||
expectedTrackedPVCs: sets.NewString(key("foo", "bar"), key("foo", "baz")),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
action *fakeAction
|
||||
backup = new(Request)
|
||||
groupResource = schema.ParseGroupResource("resource.group")
|
||||
backedUpItems = make(map[itemKey]struct{})
|
||||
w = &fakeTarWriter{}
|
||||
)
|
||||
|
||||
backup.Backup = new(v1.Backup)
|
||||
backup.NamespaceIncludesExcludes = collections.NewIncludesExcludes()
|
||||
backup.ResourceIncludesExcludes = collections.NewIncludesExcludes()
|
||||
backup.SnapshotLocations = []*v1.VolumeSnapshotLocation{
|
||||
new(v1.VolumeSnapshotLocation),
|
||||
}
|
||||
|
||||
if test.groupResource != "" {
|
||||
groupResource = schema.ParseGroupResource(test.groupResource)
|
||||
}
|
||||
|
||||
item, err := velerotest.GetAsMap(test.item)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
namespaces := test.namespaceIncludesExcludes
|
||||
if namespaces == nil {
|
||||
namespaces = collections.NewIncludesExcludes()
|
||||
}
|
||||
|
||||
if test.tarHeaderWriteError {
|
||||
w.writeHeaderError = errors.New("error")
|
||||
}
|
||||
if test.tarWriteError {
|
||||
w.writeError = errors.New("error")
|
||||
}
|
||||
|
||||
if test.customAction {
|
||||
action = &fakeAction{
|
||||
additionalItems: test.customActionAdditionalItemIdentifiers,
|
||||
}
|
||||
backup.ResolvedActions = []resolvedAction{
|
||||
{
|
||||
BackupItemAction: action,
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes(),
|
||||
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes(groupResource.String()),
|
||||
selector: labels.Everything(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
|
||||
defer podCommandExecutor.AssertExpectations(t)
|
||||
|
||||
dynamicFactory := &velerotest.FakeDynamicFactory{}
|
||||
defer dynamicFactory.AssertExpectations(t)
|
||||
|
||||
discoveryHelper := velerotest.NewFakeDiscoveryHelper(true, nil)
|
||||
|
||||
volumeSnapshotterGetter := &volumeSnapshotterGetter{}
|
||||
|
||||
b := (&defaultItemBackupperFactory{}).newItemBackupper(
|
||||
backup,
|
||||
backedUpItems,
|
||||
podCommandExecutor,
|
||||
w,
|
||||
dynamicFactory,
|
||||
discoveryHelper,
|
||||
nil, // restic backupper
|
||||
newPVCSnapshotTracker(),
|
||||
volumeSnapshotterGetter,
|
||||
).(*defaultItemBackupper)
|
||||
|
||||
var volumeSnapshotter *velerotest.FakeVolumeSnapshotter
|
||||
if test.snapshottableVolumes != nil {
|
||||
volumeSnapshotter = &velerotest.FakeVolumeSnapshotter{
|
||||
SnapshottableVolumes: test.snapshottableVolumes,
|
||||
VolumeID: "vol-abc123",
|
||||
Error: test.snapshotError,
|
||||
}
|
||||
|
||||
volumeSnapshotterGetter.volumeSnapshotter = volumeSnapshotter
|
||||
}
|
||||
|
||||
if test.trackedPVCs != nil {
|
||||
b.resticSnapshotTracker.pvcs = test.trackedPVCs
|
||||
}
|
||||
|
||||
// make sure the podCommandExecutor was set correctly in the real hook handler
|
||||
assert.Equal(t, podCommandExecutor, b.itemHookHandler.(*defaultItemHookHandler).podCommandExecutor)
|
||||
|
||||
itemHookHandler := &mockItemHookHandler{}
|
||||
defer itemHookHandler.AssertExpectations(t)
|
||||
b.itemHookHandler = itemHookHandler
|
||||
|
||||
additionalItemBackupper := &mockItemBackupper{}
|
||||
defer additionalItemBackupper.AssertExpectations(t)
|
||||
b.additionalItemBackupper = additionalItemBackupper
|
||||
|
||||
obj := &unstructured.Unstructured{Object: item}
|
||||
itemHookHandler.On("handleHooks", mock.Anything, groupResource, obj, backup.ResourceHooks, hookPhasePre).Return(nil)
|
||||
itemHookHandler.On("handleHooks", mock.Anything, groupResource, obj, backup.ResourceHooks, hookPhasePost).Return(nil)
|
||||
|
||||
for i, item := range test.customActionAdditionalItemIdentifiers {
|
||||
if test.additionalItemError != nil && i > 0 {
|
||||
break
|
||||
}
|
||||
itemClient := &velerotest.FakeDynamicClient{}
|
||||
defer itemClient.AssertExpectations(t)
|
||||
|
||||
dynamicFactory.On("ClientForGroupVersionResource", item.GroupResource.WithVersion("").GroupVersion(), metav1.APIResource{Name: item.Resource}, item.Namespace).Return(itemClient, nil)
|
||||
|
||||
itemClient.On("Get", item.Name, metav1.GetOptions{}).Return(test.customActionAdditionalItems[i], nil)
|
||||
|
||||
additionalItemBackupper.On("backupItem", mock.AnythingOfType("*logrus.Entry"), test.customActionAdditionalItems[i], item.GroupResource).Return(test.additionalItemError)
|
||||
}
|
||||
|
||||
err = b.backupItem(velerotest.NewLogger(), obj, groupResource)
|
||||
gotError := err != nil
|
||||
if e, a := test.expectError, gotError; e != a {
|
||||
t.Fatalf("error: expected %t, got %t: %v", e, a, err)
|
||||
}
|
||||
if test.expectError {
|
||||
return
|
||||
}
|
||||
|
||||
if test.expectExcluded {
|
||||
if len(w.headers) > 0 {
|
||||
t.Errorf("unexpected header write")
|
||||
}
|
||||
if len(w.data) > 0 {
|
||||
t.Errorf("unexpected data write")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Convert to JSON for comparing number of bytes to the tar header
|
||||
itemJSON, err := json.Marshal(&item)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.Equal(t, 1, len(w.headers), "headers")
|
||||
assert.Equal(t, test.expectedTarHeaderName, w.headers[0].Name, "header.name")
|
||||
assert.Equal(t, int64(len(itemJSON)), w.headers[0].Size, "header.size")
|
||||
assert.Equal(t, byte(tar.TypeReg), w.headers[0].Typeflag, "header.typeflag")
|
||||
assert.Equal(t, int64(0755), w.headers[0].Mode, "header.mode")
|
||||
assert.False(t, w.headers[0].ModTime.IsZero(), "header.modTime set")
|
||||
assert.Equal(t, 1, len(w.data), "# of data")
|
||||
|
||||
actual, err := velerotest.GetAsMap(string(w.data[0]))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if e, a := item, actual; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("data: expected %s, got %s", e, a)
|
||||
}
|
||||
|
||||
if test.customAction {
|
||||
if len(action.ids) != 1 {
|
||||
t.Errorf("unexpected custom action ids: %v", action.ids)
|
||||
} else if e, a := test.expectedActionID, action.ids[0]; e != a {
|
||||
t.Errorf("action.ids[0]: expected %s, got %s", e, a)
|
||||
}
|
||||
|
||||
require.Equal(t, 1, len(action.backups), "unexpected custom action backups: %#v", action.backups)
|
||||
assert.Equal(t, backup.Backup, &(action.backups[0]), "backup")
|
||||
}
|
||||
|
||||
if test.snapshottableVolumes != nil {
|
||||
require.Equal(t, len(test.snapshottableVolumes), len(volumeSnapshotter.SnapshotsTaken))
|
||||
}
|
||||
|
||||
if len(test.snapshottableVolumes) > 0 {
|
||||
require.Len(t, backup.VolumeSnapshots, 1)
|
||||
snapshot := backup.VolumeSnapshots[0]
|
||||
|
||||
assert.Equal(t, test.snapshottableVolumes["vol-abc123"].SnapshotID, snapshot.Status.ProviderSnapshotID)
|
||||
assert.Equal(t, test.snapshottableVolumes["vol-abc123"].Type, snapshot.Spec.VolumeType)
|
||||
assert.Equal(t, test.snapshottableVolumes["vol-abc123"].Iops, snapshot.Spec.VolumeIOPS)
|
||||
assert.Equal(t, test.snapshottableVolumes["vol-abc123"].AvailabilityZone, snapshot.Spec.VolumeAZ)
|
||||
}
|
||||
|
||||
if test.expectedTrackedPVCs != nil {
|
||||
require.Equal(t, len(test.expectedTrackedPVCs), len(b.resticSnapshotTracker.pvcs))
|
||||
|
||||
for key := range test.expectedTrackedPVCs {
|
||||
assert.True(t, b.resticSnapshotTracker.pvcs.Has(key))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type volumeSnapshotterGetter struct {
|
||||
volumeSnapshotter velero.VolumeSnapshotter
|
||||
}
|
||||
|
||||
func (b *volumeSnapshotterGetter) GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error) {
|
||||
if b.volumeSnapshotter != nil {
|
||||
return b.volumeSnapshotter, nil
|
||||
}
|
||||
return nil, errors.New("plugin not found")
|
||||
}
|
||||
|
||||
type addAnnotationAction struct{}
|
||||
|
||||
func (a *addAnnotationAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
|
||||
// since item actions run out-of-proc, do a deep-copy here to simulate passing data
|
||||
// across a process boundary.
|
||||
copy := item.(*unstructured.Unstructured).DeepCopy()
|
||||
|
||||
metadata, err := meta.Accessor(copy)
|
||||
if err != nil {
|
||||
return copy, nil, nil
|
||||
}
|
||||
|
||||
annotations := metadata.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = make(map[string]string)
|
||||
}
|
||||
annotations["foo"] = "bar"
|
||||
metadata.SetAnnotations(annotations)
|
||||
|
||||
return copy, nil, nil
|
||||
}
|
||||
|
||||
func (a *addAnnotationAction) AppliesTo() (velero.ResourceSelector, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func TestItemActionModificationsToItemPersist(t *testing.T) {
|
||||
var (
|
||||
w = &fakeTarWriter{}
|
||||
obj = &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": "myns",
|
||||
"name": "bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
req = &Request{
|
||||
NamespaceIncludesExcludes: collections.NewIncludesExcludes(),
|
||||
ResourceIncludesExcludes: collections.NewIncludesExcludes(),
|
||||
ResolvedActions: []resolvedAction{
|
||||
{
|
||||
BackupItemAction: &addAnnotationAction{},
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes(),
|
||||
resourceIncludesExcludes: collections.NewIncludesExcludes(),
|
||||
selector: labels.Everything(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
b = (&defaultItemBackupperFactory{}).newItemBackupper(
|
||||
req,
|
||||
make(map[itemKey]struct{}),
|
||||
nil,
|
||||
w,
|
||||
&velerotest.FakeDynamicFactory{},
|
||||
velerotest.NewFakeDiscoveryHelper(true, nil),
|
||||
nil,
|
||||
newPVCSnapshotTracker(),
|
||||
nil,
|
||||
).(*defaultItemBackupper)
|
||||
)
|
||||
|
||||
// our expected backed-up object is the passed-in object plus the annotation
|
||||
// that the backup item action adds.
|
||||
expected := obj.DeepCopy()
|
||||
expected.SetAnnotations(map[string]string{"foo": "bar"})
|
||||
|
||||
// method under test
|
||||
require.NoError(t, b.backupItem(velerotest.NewLogger(), obj, schema.ParseGroupResource("resource.group")))
|
||||
|
||||
// get the actual backed-up item
|
||||
require.Len(t, w.data, 1)
|
||||
actual, err := velerotest.GetAsMap(string(w.data[0]))
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, expected.Object, actual)
|
||||
}
|
||||
|
||||
func TestResticAnnotationsPersist(t *testing.T) {
|
||||
var (
|
||||
w = &fakeTarWriter{}
|
||||
obj = &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"namespace": "myns",
|
||||
"name": "bar",
|
||||
"annotations": map[string]interface{}{
|
||||
"backup.velero.io/backup-volumes": "volume-1,volume-2",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
req = &Request{
|
||||
NamespaceIncludesExcludes: collections.NewIncludesExcludes(),
|
||||
ResourceIncludesExcludes: collections.NewIncludesExcludes(),
|
||||
ResolvedActions: []resolvedAction{
|
||||
{
|
||||
BackupItemAction: &addAnnotationAction{},
|
||||
namespaceIncludesExcludes: collections.NewIncludesExcludes(),
|
||||
resourceIncludesExcludes: collections.NewIncludesExcludes(),
|
||||
selector: labels.Everything(),
|
||||
},
|
||||
},
|
||||
}
|
||||
resticBackupper = &resticmocks.Backupper{}
|
||||
b = (&defaultItemBackupperFactory{}).newItemBackupper(
|
||||
req,
|
||||
make(map[itemKey]struct{}),
|
||||
nil,
|
||||
w,
|
||||
&velerotest.FakeDynamicFactory{},
|
||||
velerotest.NewFakeDiscoveryHelper(true, nil),
|
||||
resticBackupper,
|
||||
newPVCSnapshotTracker(),
|
||||
nil,
|
||||
).(*defaultItemBackupper)
|
||||
)
|
||||
|
||||
resticBackupper.
|
||||
On("BackupPodVolumes", mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(map[string]string{"volume-1": "snapshot-1", "volume-2": "snapshot-2"}, nil)
|
||||
|
||||
// our expected backed-up object is the passed-in object, plus the annotation
|
||||
// that the backup item action adds, plus the annotations that the restic
|
||||
// backupper adds
|
||||
expected := obj.DeepCopy()
|
||||
annotations := expected.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = make(map[string]string)
|
||||
}
|
||||
annotations["foo"] = "bar"
|
||||
annotations["snapshot.velero.io/volume-1"] = "snapshot-1"
|
||||
annotations["snapshot.velero.io/volume-2"] = "snapshot-2"
|
||||
expected.SetAnnotations(annotations)
|
||||
|
||||
// method under test
|
||||
require.NoError(t, b.backupItem(velerotest.NewLogger(), obj, schema.ParseGroupResource("pods")))
|
||||
|
||||
// get the actual backed-up item
|
||||
require.Len(t, w.data, 1)
|
||||
actual, err := velerotest.GetAsMap(string(w.data[0]))
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, expected.Object, actual)
|
||||
}
|
||||
|
||||
func TestTakePVSnapshot(t *testing.T) {
|
||||
iops := int64(1000)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
snapshotEnabled bool
|
||||
pv string
|
||||
ttl time.Duration
|
||||
expectError bool
|
||||
expectedVolumeID string
|
||||
expectedSnapshotsTaken int
|
||||
volumeInfo map[string]velerotest.VolumeBackupInfo
|
||||
}{
|
||||
{
|
||||
name: "snapshot disabled",
|
||||
pv: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv"}}`,
|
||||
snapshotEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "unsupported PV source type",
|
||||
snapshotEnabled: true,
|
||||
pv: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv"}, "spec": {"unsupportedPVSource": {}}}`,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "without iops",
|
||||
snapshotEnabled: true,
|
||||
pv: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
|
||||
expectError: false,
|
||||
expectedSnapshotsTaken: 1,
|
||||
expectedVolumeID: "vol-abc123",
|
||||
ttl: 5 * time.Minute,
|
||||
volumeInfo: map[string]velerotest.VolumeBackupInfo{
|
||||
"vol-abc123": {Type: "gp", SnapshotID: "snap-1", AvailabilityZone: "us-east-1c"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with iops",
|
||||
snapshotEnabled: true,
|
||||
pv: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
|
||||
expectError: false,
|
||||
expectedSnapshotsTaken: 1,
|
||||
expectedVolumeID: "vol-abc123",
|
||||
ttl: 5 * time.Minute,
|
||||
volumeInfo: map[string]velerotest.VolumeBackupInfo{
|
||||
"vol-abc123": {Type: "io1", Iops: &iops, SnapshotID: "snap-1", AvailabilityZone: "us-east-1c"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create snapshot error",
|
||||
snapshotEnabled: true,
|
||||
pv: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv"}, "spec": {"gcePersistentDisk": {"pdName": "pd-abc123"}}}`,
|
||||
expectedVolumeID: "pd-abc123",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "PV with label metadata but no failureDomainZone",
|
||||
snapshotEnabled: true,
|
||||
pv: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/region": "us-east-1"}}, "spec": {"awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`,
|
||||
expectError: false,
|
||||
expectedSnapshotsTaken: 1,
|
||||
expectedVolumeID: "vol-abc123",
|
||||
ttl: 5 * time.Minute,
|
||||
volumeInfo: map[string]velerotest.VolumeBackupInfo{
|
||||
"vol-abc123": {Type: "gp", SnapshotID: "snap-1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
backup := &v1.Backup{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: v1.DefaultNamespace,
|
||||
Name: "mybackup",
|
||||
},
|
||||
Spec: v1.BackupSpec{
|
||||
SnapshotVolumes: &test.snapshotEnabled,
|
||||
TTL: metav1.Duration{Duration: test.ttl},
|
||||
},
|
||||
}
|
||||
|
||||
volumeSnapshotter := &velerotest.FakeVolumeSnapshotter{
|
||||
SnapshottableVolumes: test.volumeInfo,
|
||||
VolumeID: test.expectedVolumeID,
|
||||
}
|
||||
|
||||
ib := &defaultItemBackupper{
|
||||
backupRequest: &Request{
|
||||
Backup: backup,
|
||||
SnapshotLocations: []*v1.VolumeSnapshotLocation{new(v1.VolumeSnapshotLocation)},
|
||||
},
|
||||
volumeSnapshotterGetter: &volumeSnapshotterGetter{volumeSnapshotter: volumeSnapshotter},
|
||||
}
|
||||
|
||||
pv, err := velerotest.GetAsMap(test.pv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// method under test
|
||||
err = ib.takePVSnapshot(&unstructured.Unstructured{Object: pv}, velerotest.NewLogger())
|
||||
|
||||
gotErr := err != nil
|
||||
|
||||
if e, a := test.expectError, gotErr; e != a {
|
||||
t.Errorf("error: expected %v, got %v", e, a)
|
||||
}
|
||||
if test.expectError {
|
||||
return
|
||||
}
|
||||
|
||||
if !test.snapshotEnabled {
|
||||
// don't need to check anything else if snapshots are disabled
|
||||
return
|
||||
}
|
||||
|
||||
// we should have exactly one snapshot taken
|
||||
require.Equal(t, test.expectedSnapshotsTaken, volumeSnapshotter.SnapshotsTaken.Len())
|
||||
|
||||
if test.expectedSnapshotsTaken > 0 {
|
||||
require.Len(t, ib.backupRequest.VolumeSnapshots, 1)
|
||||
snapshot := ib.backupRequest.VolumeSnapshots[0]
|
||||
|
||||
snapshotID, _ := volumeSnapshotter.SnapshotsTaken.PopAny()
|
||||
assert.Equal(t, snapshotID, snapshot.Status.ProviderSnapshotID)
|
||||
assert.Equal(t, test.volumeInfo[test.expectedVolumeID].Type, snapshot.Spec.VolumeType)
|
||||
assert.Equal(t, test.volumeInfo[test.expectedVolumeID].Iops, snapshot.Spec.VolumeIOPS)
|
||||
assert.Equal(t, test.volumeInfo[test.expectedVolumeID].AvailabilityZone, snapshot.Spec.VolumeAZ)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type fakeTarWriter struct {
|
||||
closeCalled bool
|
||||
headers []*tar.Header
|
||||
data [][]byte
|
||||
writeHeaderError error
|
||||
writeError error
|
||||
}
|
||||
|
||||
func (w *fakeTarWriter) Close() error { return nil }
|
||||
|
||||
func (w *fakeTarWriter) Write(data []byte) (int, error) {
|
||||
w.data = append(w.data, data)
|
||||
return 0, w.writeError
|
||||
}
|
||||
|
||||
func (w *fakeTarWriter) WriteHeader(header *tar.Header) error {
|
||||
w.headers = append(w.headers, header)
|
||||
return w.writeHeaderError
|
||||
}
|
||||
|
||||
type mockItemBackupper struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (ib *mockItemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstructured, groupResource schema.GroupResource) error {
|
||||
args := ib.Called(logger, obj, groupResource)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
@@ -694,3 +694,11 @@ func TestResourceHookApplicableTo(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func parseLabelSelectorOrDie(s string) labels.Selector {
|
||||
ret, err := labels.Parse(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -1,11 +1,35 @@
|
||||
/*
|
||||
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 backup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
"github.com/heptio/velero/pkg/util/collections"
|
||||
"github.com/heptio/velero/pkg/volume"
|
||||
)
|
||||
|
||||
type itemKey struct {
|
||||
resource string
|
||||
namespace string
|
||||
name string
|
||||
}
|
||||
|
||||
// Request is a request for a backup, with all references to other objects
|
||||
// materialized (e.g. backup/snapshot locations, includes/excludes, etc.)
|
||||
type Request struct {
|
||||
@@ -18,5 +42,21 @@ type Request struct {
|
||||
ResourceHooks []resourceHook
|
||||
ResolvedActions []resolvedAction
|
||||
|
||||
VolumeSnapshots []*volume.Snapshot
|
||||
VolumeSnapshots []*volume.Snapshot
|
||||
PodVolumeBackups []*velerov1api.PodVolumeBackup
|
||||
BackedUpItems map[itemKey]struct{}
|
||||
}
|
||||
|
||||
// BackupResourceList returns the list of backed up resources grouped by the API
|
||||
// Version and Kind
|
||||
func (r *Request) BackupResourceList() map[string][]string {
|
||||
resources := map[string][]string{}
|
||||
for i := range r.BackedUpItems {
|
||||
entry := i.name
|
||||
if i.namespace != "" {
|
||||
entry = fmt.Sprintf("%s/%s", i.namespace, i.name)
|
||||
}
|
||||
resources[i.resource] = append(resources[i.resource], entry)
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
58
pkg/backup/request_test.go
Normal file
58
pkg/backup/request_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
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 backup
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRequest_BackupResourceList(t *testing.T) {
|
||||
items := []itemKey{
|
||||
{
|
||||
resource: "apps/v1/Deployment",
|
||||
name: "my-deploy",
|
||||
namespace: "default",
|
||||
},
|
||||
{
|
||||
resource: "v1/Pod",
|
||||
name: "pod1",
|
||||
namespace: "ns1",
|
||||
},
|
||||
{
|
||||
resource: "v1/Pod",
|
||||
name: "pod2",
|
||||
namespace: "ns2",
|
||||
},
|
||||
{
|
||||
resource: "v1/PersistentVolume",
|
||||
name: "my-pv",
|
||||
},
|
||||
}
|
||||
backedUpItems := map[itemKey]struct{}{}
|
||||
for _, it := range items {
|
||||
backedUpItems[it] = struct{}{}
|
||||
}
|
||||
|
||||
req := Request{BackedUpItems: backedUpItems}
|
||||
assert.Equal(t, req.BackupResourceList(), map[string][]string{
|
||||
"apps/v1/Deployment": {"default/my-deploy"},
|
||||
"v1/Pod": {"ns1/pod1", "ns2/pod2"},
|
||||
"v1/PersistentVolume": {"my-pv"},
|
||||
})
|
||||
}
|
||||
@@ -40,7 +40,6 @@ type resourceBackupperFactory interface {
|
||||
backupRequest *Request,
|
||||
dynamicFactory client.DynamicFactory,
|
||||
discoveryHelper discovery.Helper,
|
||||
backedUpItems map[itemKey]struct{},
|
||||
cohabitatingResources map[string]*cohabitatingResource,
|
||||
podCommandExecutor podexec.PodCommandExecutor,
|
||||
tarWriter tarWriter,
|
||||
@@ -57,7 +56,6 @@ func (f *defaultResourceBackupperFactory) newResourceBackupper(
|
||||
backupRequest *Request,
|
||||
dynamicFactory client.DynamicFactory,
|
||||
discoveryHelper discovery.Helper,
|
||||
backedUpItems map[itemKey]struct{},
|
||||
cohabitatingResources map[string]*cohabitatingResource,
|
||||
podCommandExecutor podexec.PodCommandExecutor,
|
||||
tarWriter tarWriter,
|
||||
@@ -70,7 +68,6 @@ func (f *defaultResourceBackupperFactory) newResourceBackupper(
|
||||
backupRequest: backupRequest,
|
||||
dynamicFactory: dynamicFactory,
|
||||
discoveryHelper: discoveryHelper,
|
||||
backedUpItems: backedUpItems,
|
||||
cohabitatingResources: cohabitatingResources,
|
||||
podCommandExecutor: podCommandExecutor,
|
||||
tarWriter: tarWriter,
|
||||
@@ -91,7 +88,6 @@ type defaultResourceBackupper struct {
|
||||
backupRequest *Request
|
||||
dynamicFactory client.DynamicFactory
|
||||
discoveryHelper discovery.Helper
|
||||
backedUpItems map[itemKey]struct{}
|
||||
cohabitatingResources map[string]*cohabitatingResource
|
||||
podCommandExecutor podexec.PodCommandExecutor
|
||||
tarWriter tarWriter
|
||||
@@ -156,7 +152,6 @@ func (rb *defaultResourceBackupper) backupResource(group *metav1.APIResourceList
|
||||
|
||||
itemBackupper := rb.itemBackupperFactory.newItemBackupper(
|
||||
rb.backupRequest,
|
||||
rb.backedUpItems,
|
||||
rb.podCommandExecutor,
|
||||
rb.tarWriter,
|
||||
rb.dynamicFactory,
|
||||
@@ -221,9 +216,9 @@ func (rb *defaultResourceBackupper) backupResource(group *metav1.APIResourceList
|
||||
continue
|
||||
}
|
||||
|
||||
var labelSelector string
|
||||
labelSelector := "velero.io/exclude-from-backup!=true"
|
||||
if selector := rb.backupRequest.Spec.LabelSelector; selector != nil {
|
||||
labelSelector = metav1.FormatLabelSelector(selector)
|
||||
labelSelector = labelSelector + "," + metav1.FormatLabelSelector(selector)
|
||||
}
|
||||
|
||||
log.Info("Listing items")
|
||||
|
||||
@@ -1,665 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package backup
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"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/schema"
|
||||
|
||||
v1 "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
"github.com/heptio/velero/pkg/client"
|
||||
"github.com/heptio/velero/pkg/discovery"
|
||||
"github.com/heptio/velero/pkg/kuberesource"
|
||||
"github.com/heptio/velero/pkg/podexec"
|
||||
"github.com/heptio/velero/pkg/restic"
|
||||
"github.com/heptio/velero/pkg/util/collections"
|
||||
velerotest "github.com/heptio/velero/pkg/util/test"
|
||||
)
|
||||
|
||||
func TestBackupResource(t *testing.T) {
|
||||
var (
|
||||
trueVal = true
|
||||
falseVal = false
|
||||
truePointer = &trueVal
|
||||
falsePointer = &falseVal
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
namespaces *collections.IncludesExcludes
|
||||
resources *collections.IncludesExcludes
|
||||
expectSkip bool
|
||||
expectedListedNamespaces []string
|
||||
apiGroup *metav1.APIResourceList
|
||||
apiResource metav1.APIResource
|
||||
groupVersion schema.GroupVersion
|
||||
groupResource schema.GroupResource
|
||||
listResponses [][]*unstructured.Unstructured
|
||||
getResponses []*unstructured.Unstructured
|
||||
includeClusterResources *bool
|
||||
}{
|
||||
{
|
||||
name: "resource not included",
|
||||
apiGroup: v1Group,
|
||||
apiResource: podsResource,
|
||||
resources: collections.NewIncludesExcludes().Excludes("pods"),
|
||||
expectSkip: true,
|
||||
},
|
||||
{
|
||||
name: "list all namespaces",
|
||||
namespaces: collections.NewIncludesExcludes(),
|
||||
resources: collections.NewIncludesExcludes(),
|
||||
expectedListedNamespaces: []string{""},
|
||||
apiGroup: v1Group,
|
||||
apiResource: podsResource,
|
||||
groupVersion: schema.GroupVersion{Group: "", Version: "v1"},
|
||||
groupResource: schema.GroupResource{Group: "", Resource: "pods"},
|
||||
listResponses: [][]*unstructured.Unstructured{
|
||||
{
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Pod","metadata":{"namespace":"myns","name":"myname1"}}`),
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Pod","metadata":{"namespace":"myns","name":"myname2"}}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list selected namespaces",
|
||||
namespaces: collections.NewIncludesExcludes().Includes("a", "b"),
|
||||
resources: collections.NewIncludesExcludes(),
|
||||
expectedListedNamespaces: []string{"a", "b"},
|
||||
apiGroup: v1Group,
|
||||
apiResource: podsResource,
|
||||
groupVersion: schema.GroupVersion{Group: "", Version: "v1"},
|
||||
groupResource: schema.GroupResource{Group: "", Resource: "pods"},
|
||||
listResponses: [][]*unstructured.Unstructured{
|
||||
{
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Pod","metadata":{"namespace":"a","name":"myname1"}}`),
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Pod","metadata":{"namespace":"a","name":"myname2"}}`),
|
||||
},
|
||||
{
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Pod","metadata":{"namespace":"b","name":"myname3"}}`),
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Pod","metadata":{"namespace":"b","name":"myname4"}}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list all namespaces - cluster scoped",
|
||||
namespaces: collections.NewIncludesExcludes(),
|
||||
resources: collections.NewIncludesExcludes(),
|
||||
expectedListedNamespaces: []string{""},
|
||||
apiGroup: certificatesGroup,
|
||||
apiResource: certificateSigningRequestsResource,
|
||||
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
|
||||
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
|
||||
listResponses: [][]*unstructured.Unstructured{
|
||||
{
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname1"}}`),
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname2"}}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should include cluster-scoped resource if backing up subset of namespaces and --include-cluster-resources=true",
|
||||
namespaces: collections.NewIncludesExcludes().Includes("ns-1"),
|
||||
resources: collections.NewIncludesExcludes(),
|
||||
includeClusterResources: truePointer,
|
||||
expectedListedNamespaces: []string{""},
|
||||
apiGroup: certificatesGroup,
|
||||
apiResource: certificateSigningRequestsResource,
|
||||
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
|
||||
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
|
||||
listResponses: [][]*unstructured.Unstructured{
|
||||
{
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname1"}}`),
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname2"}}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should not include cluster-scoped resource if backing up subset of namespaces and --include-cluster-resources=false",
|
||||
namespaces: collections.NewIncludesExcludes().Includes("ns-1"),
|
||||
resources: collections.NewIncludesExcludes(),
|
||||
includeClusterResources: falsePointer,
|
||||
apiGroup: certificatesGroup,
|
||||
apiResource: certificateSigningRequestsResource,
|
||||
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
|
||||
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
|
||||
expectSkip: true,
|
||||
},
|
||||
{
|
||||
name: "should not include cluster-scoped resource if backing up subset of namespaces and --include-cluster-resources=nil",
|
||||
namespaces: collections.NewIncludesExcludes().Includes("ns-1"),
|
||||
resources: collections.NewIncludesExcludes(),
|
||||
includeClusterResources: nil,
|
||||
apiGroup: certificatesGroup,
|
||||
apiResource: certificateSigningRequestsResource,
|
||||
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
|
||||
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
|
||||
expectSkip: true,
|
||||
},
|
||||
{
|
||||
name: "should include cluster-scoped resource if backing up all namespaces and --include-cluster-resources=true",
|
||||
namespaces: collections.NewIncludesExcludes(),
|
||||
resources: collections.NewIncludesExcludes(),
|
||||
includeClusterResources: truePointer,
|
||||
expectedListedNamespaces: []string{""},
|
||||
apiGroup: certificatesGroup,
|
||||
apiResource: certificateSigningRequestsResource,
|
||||
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
|
||||
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
|
||||
listResponses: [][]*unstructured.Unstructured{
|
||||
{
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname1"}}`),
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname2"}}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should not include cluster-scoped resource if backing up all namespaces and --include-cluster-resources=false",
|
||||
namespaces: collections.NewIncludesExcludes(),
|
||||
resources: collections.NewIncludesExcludes(),
|
||||
includeClusterResources: falsePointer,
|
||||
apiGroup: certificatesGroup,
|
||||
apiResource: certificateSigningRequestsResource,
|
||||
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
|
||||
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
|
||||
expectSkip: true,
|
||||
},
|
||||
{
|
||||
name: "should include cluster-scoped resource if backing up all namespaces and --include-cluster-resources=nil",
|
||||
namespaces: collections.NewIncludesExcludes(),
|
||||
resources: collections.NewIncludesExcludes(),
|
||||
includeClusterResources: nil,
|
||||
expectedListedNamespaces: []string{""},
|
||||
apiGroup: certificatesGroup,
|
||||
apiResource: certificateSigningRequestsResource,
|
||||
groupVersion: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1beta1"},
|
||||
groupResource: schema.GroupResource{Group: "certificates.k8s.io", Resource: "certificatesigningrequests"},
|
||||
listResponses: [][]*unstructured.Unstructured{
|
||||
{
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname1"}}`),
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"certificates.k8s.io/v1beta1","kind":"CertificateSigningRequest","metadata":{"name":"myname2"}}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should include specified namespaces if backing up subset of namespaces and --include-cluster-resources=nil",
|
||||
namespaces: collections.NewIncludesExcludes().Includes("ns-1", "ns-2"),
|
||||
resources: collections.NewIncludesExcludes(),
|
||||
includeClusterResources: nil,
|
||||
expectedListedNamespaces: []string{"ns-1", "ns-2"},
|
||||
apiGroup: v1Group,
|
||||
apiResource: namespacesResource,
|
||||
groupVersion: schema.GroupVersion{Group: "", Version: "v1"},
|
||||
groupResource: schema.GroupResource{Group: "", Resource: "namespaces"},
|
||||
expectSkip: false,
|
||||
getResponses: []*unstructured.Unstructured{
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-1"}}`),
|
||||
velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-2"}}`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
req := &Request{
|
||||
Backup: &v1.Backup{
|
||||
Spec: v1.BackupSpec{
|
||||
IncludeClusterResources: test.includeClusterResources,
|
||||
},
|
||||
},
|
||||
ResolvedActions: []resolvedAction{
|
||||
{
|
||||
BackupItemAction: newFakeAction("pods"),
|
||||
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes("pods"),
|
||||
},
|
||||
},
|
||||
ResourceHooks: []resourceHook{
|
||||
{name: "myhook"},
|
||||
},
|
||||
ResourceIncludesExcludes: test.resources,
|
||||
NamespaceIncludesExcludes: test.namespaces,
|
||||
}
|
||||
|
||||
dynamicFactory := &velerotest.FakeDynamicFactory{}
|
||||
defer dynamicFactory.AssertExpectations(t)
|
||||
|
||||
discoveryHelper := velerotest.NewFakeDiscoveryHelper(true, nil)
|
||||
|
||||
backedUpItems := map[itemKey]struct{}{
|
||||
{resource: "foo", namespace: "ns", name: "name"}: {},
|
||||
}
|
||||
|
||||
cohabitatingResources := map[string]*cohabitatingResource{
|
||||
"deployments": newCohabitatingResource("deployments", "extensions", "apps"),
|
||||
"networkpolicies": newCohabitatingResource("networkpolicies", "extensions", "networking.k8s.io"),
|
||||
}
|
||||
|
||||
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
|
||||
defer podCommandExecutor.AssertExpectations(t)
|
||||
|
||||
tarWriter := &fakeTarWriter{}
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
rb := (&defaultResourceBackupperFactory{}).newResourceBackupper(
|
||||
velerotest.NewLogger(),
|
||||
req,
|
||||
dynamicFactory,
|
||||
discoveryHelper,
|
||||
backedUpItems,
|
||||
cohabitatingResources,
|
||||
podCommandExecutor,
|
||||
tarWriter,
|
||||
nil, // restic backupper
|
||||
newPVCSnapshotTracker(),
|
||||
nil,
|
||||
).(*defaultResourceBackupper)
|
||||
|
||||
itemBackupperFactory := &mockItemBackupperFactory{}
|
||||
defer itemBackupperFactory.AssertExpectations(t)
|
||||
rb.itemBackupperFactory = itemBackupperFactory
|
||||
|
||||
if !test.expectSkip {
|
||||
itemBackupper := &mockItemBackupper{}
|
||||
defer itemBackupper.AssertExpectations(t)
|
||||
|
||||
itemBackupperFactory.On("newItemBackupper",
|
||||
req,
|
||||
backedUpItems,
|
||||
podCommandExecutor,
|
||||
tarWriter,
|
||||
dynamicFactory,
|
||||
discoveryHelper,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
).Return(itemBackupper)
|
||||
|
||||
if len(test.listResponses) > 0 {
|
||||
for i, namespace := range test.expectedListedNamespaces {
|
||||
client := &velerotest.FakeDynamicClient{}
|
||||
defer client.AssertExpectations(t)
|
||||
|
||||
dynamicFactory.On("ClientForGroupVersionResource", test.groupVersion, test.apiResource, namespace).Return(client, nil)
|
||||
|
||||
list := &unstructured.UnstructuredList{
|
||||
Items: []unstructured.Unstructured{},
|
||||
}
|
||||
for _, item := range test.listResponses[i] {
|
||||
list.Items = append(list.Items, *item)
|
||||
itemBackupper.On("backupItem", mock.AnythingOfType("*logrus.Entry"), item, test.groupResource).Return(nil)
|
||||
}
|
||||
client.On("List", metav1.ListOptions{}).Return(list, nil)
|
||||
}
|
||||
}
|
||||
|
||||
if len(test.getResponses) > 0 {
|
||||
client := &velerotest.FakeDynamicClient{}
|
||||
defer client.AssertExpectations(t)
|
||||
|
||||
dynamicFactory.On("ClientForGroupVersionResource", test.groupVersion, test.apiResource, "").Return(client, nil)
|
||||
|
||||
for i, namespace := range test.expectedListedNamespaces {
|
||||
item := test.getResponses[i]
|
||||
client.On("Get", namespace, metav1.GetOptions{}).Return(item, nil)
|
||||
itemBackupper.On("backupItem", mock.AnythingOfType("*logrus.Entry"), item, test.groupResource).Return(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := rb.backupResource(test.apiGroup, test.apiResource)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackupResourceCohabitation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
apiResource metav1.APIResource
|
||||
apiGroup1 *metav1.APIResourceList
|
||||
groupVersion1 schema.GroupVersion
|
||||
apiGroup2 *metav1.APIResourceList
|
||||
groupVersion2 schema.GroupVersion
|
||||
}{
|
||||
{
|
||||
name: "deployments - extensions first",
|
||||
apiResource: deploymentsResource,
|
||||
apiGroup1: extensionsGroup,
|
||||
groupVersion1: extensionsGroupVersion,
|
||||
apiGroup2: appsGroup,
|
||||
groupVersion2: appsGroupVersion,
|
||||
},
|
||||
{
|
||||
name: "deployments - apps first",
|
||||
apiResource: deploymentsResource,
|
||||
apiGroup1: appsGroup,
|
||||
groupVersion1: appsGroupVersion,
|
||||
apiGroup2: extensionsGroup,
|
||||
groupVersion2: extensionsGroupVersion,
|
||||
},
|
||||
{
|
||||
name: "networkpolicies - extensions first",
|
||||
apiResource: networkPoliciesResource,
|
||||
apiGroup1: extensionsGroup,
|
||||
groupVersion1: extensionsGroupVersion,
|
||||
apiGroup2: networkingGroup,
|
||||
groupVersion2: networkingGroupVersion,
|
||||
},
|
||||
{
|
||||
name: "networkpolicies - networking first",
|
||||
apiResource: networkPoliciesResource,
|
||||
apiGroup1: networkingGroup,
|
||||
groupVersion1: networkingGroupVersion,
|
||||
apiGroup2: extensionsGroup,
|
||||
groupVersion2: extensionsGroupVersion,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := &Request{
|
||||
Backup: &v1.Backup{
|
||||
Spec: v1.BackupSpec{
|
||||
LabelSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
NamespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
ResourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
ResolvedActions: []resolvedAction{
|
||||
{
|
||||
BackupItemAction: newFakeAction("pods"),
|
||||
resourceIncludesExcludes: collections.NewIncludesExcludes().Includes("pods"),
|
||||
},
|
||||
},
|
||||
ResourceHooks: []resourceHook{
|
||||
{name: "myhook"},
|
||||
},
|
||||
}
|
||||
|
||||
dynamicFactory := &velerotest.FakeDynamicFactory{}
|
||||
defer dynamicFactory.AssertExpectations(t)
|
||||
|
||||
discoveryHelper := velerotest.NewFakeDiscoveryHelper(true, nil)
|
||||
|
||||
backedUpItems := map[itemKey]struct{}{
|
||||
{resource: "foo", namespace: "ns", name: "name"}: {},
|
||||
}
|
||||
|
||||
cohabitatingResources := map[string]*cohabitatingResource{
|
||||
"deployments": newCohabitatingResource("deployments", "extensions", "apps"),
|
||||
"networkpolicies": newCohabitatingResource("networkpolicies", "extensions", "networking.k8s.io"),
|
||||
}
|
||||
|
||||
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
|
||||
defer podCommandExecutor.AssertExpectations(t)
|
||||
|
||||
tarWriter := &fakeTarWriter{}
|
||||
|
||||
rb := (&defaultResourceBackupperFactory{}).newResourceBackupper(
|
||||
velerotest.NewLogger(),
|
||||
req,
|
||||
dynamicFactory,
|
||||
discoveryHelper,
|
||||
backedUpItems,
|
||||
cohabitatingResources,
|
||||
podCommandExecutor,
|
||||
tarWriter,
|
||||
nil, // restic backupper
|
||||
newPVCSnapshotTracker(),
|
||||
nil,
|
||||
).(*defaultResourceBackupper)
|
||||
|
||||
itemBackupperFactory := &mockItemBackupperFactory{}
|
||||
defer itemBackupperFactory.AssertExpectations(t)
|
||||
rb.itemBackupperFactory = itemBackupperFactory
|
||||
|
||||
itemBackupper := &mockItemBackupper{}
|
||||
defer itemBackupper.AssertExpectations(t)
|
||||
|
||||
itemBackupperFactory.On("newItemBackupper",
|
||||
req,
|
||||
backedUpItems,
|
||||
podCommandExecutor,
|
||||
tarWriter,
|
||||
dynamicFactory,
|
||||
discoveryHelper,
|
||||
mock.Anything, // restic backupper
|
||||
mock.Anything, // pvc snapshot tracker
|
||||
nil,
|
||||
mock.Anything,
|
||||
).Return(itemBackupper)
|
||||
|
||||
client := &velerotest.FakeDynamicClient{}
|
||||
defer client.AssertExpectations(t)
|
||||
|
||||
// STEP 1: make sure the initial backup goes through
|
||||
dynamicFactory.On("ClientForGroupVersionResource", test.groupVersion1, test.apiResource, "").Return(client, nil)
|
||||
client.On("List", metav1.ListOptions{LabelSelector: metav1.FormatLabelSelector(req.Backup.Spec.LabelSelector)}).Return(&unstructured.UnstructuredList{}, nil)
|
||||
|
||||
// STEP 2: do the backup
|
||||
err := rb.backupResource(test.apiGroup1, test.apiResource)
|
||||
require.NoError(t, err)
|
||||
|
||||
// STEP 3: try to back up the cohabitating resource
|
||||
err = rb.backupResource(test.apiGroup2, test.apiResource)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackupResourceOnlyIncludesSpecifiedNamespaces(t *testing.T) {
|
||||
req := &Request{
|
||||
Backup: &v1.Backup{},
|
||||
NamespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("ns-1"),
|
||||
ResourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
}
|
||||
|
||||
backedUpItems := map[itemKey]struct{}{}
|
||||
|
||||
dynamicFactory := &velerotest.FakeDynamicFactory{}
|
||||
defer dynamicFactory.AssertExpectations(t)
|
||||
|
||||
discoveryHelper := velerotest.NewFakeDiscoveryHelper(true, nil)
|
||||
|
||||
cohabitatingResources := map[string]*cohabitatingResource{}
|
||||
|
||||
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
|
||||
defer podCommandExecutor.AssertExpectations(t)
|
||||
|
||||
tarWriter := &fakeTarWriter{}
|
||||
|
||||
rb := (&defaultResourceBackupperFactory{}).newResourceBackupper(
|
||||
velerotest.NewLogger(),
|
||||
req,
|
||||
dynamicFactory,
|
||||
discoveryHelper,
|
||||
backedUpItems,
|
||||
cohabitatingResources,
|
||||
podCommandExecutor,
|
||||
tarWriter,
|
||||
nil, // restic backupper
|
||||
newPVCSnapshotTracker(),
|
||||
nil,
|
||||
).(*defaultResourceBackupper)
|
||||
|
||||
itemBackupperFactory := &mockItemBackupperFactory{}
|
||||
defer itemBackupperFactory.AssertExpectations(t)
|
||||
rb.itemBackupperFactory = itemBackupperFactory
|
||||
|
||||
itemHookHandler := &mockItemHookHandler{}
|
||||
defer itemHookHandler.AssertExpectations(t)
|
||||
|
||||
itemBackupper := &defaultItemBackupper{
|
||||
backupRequest: req,
|
||||
backedUpItems: backedUpItems,
|
||||
tarWriter: tarWriter,
|
||||
dynamicFactory: dynamicFactory,
|
||||
discoveryHelper: discoveryHelper,
|
||||
itemHookHandler: itemHookHandler,
|
||||
}
|
||||
|
||||
itemBackupperFactory.On("newItemBackupper",
|
||||
req,
|
||||
backedUpItems,
|
||||
podCommandExecutor,
|
||||
tarWriter,
|
||||
dynamicFactory,
|
||||
discoveryHelper,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
).Return(itemBackupper)
|
||||
|
||||
client := &velerotest.FakeDynamicClient{}
|
||||
defer client.AssertExpectations(t)
|
||||
|
||||
coreV1Group := schema.GroupVersion{Group: "", Version: "v1"}
|
||||
dynamicFactory.On("ClientForGroupVersionResource", coreV1Group, namespacesResource, "").Return(client, nil)
|
||||
ns1 := velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-1"}}`)
|
||||
client.On("Get", "ns-1", metav1.GetOptions{}).Return(ns1, nil)
|
||||
|
||||
itemHookHandler.On("handleHooks", mock.Anything, schema.GroupResource{Group: "", Resource: "namespaces"}, ns1, req.ResourceHooks, hookPhasePre).Return(nil)
|
||||
itemHookHandler.On("handleHooks", mock.Anything, schema.GroupResource{Group: "", Resource: "namespaces"}, ns1, req.ResourceHooks, hookPhasePost).Return(nil)
|
||||
|
||||
err := rb.backupResource(v1Group, namespacesResource)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, tarWriter.headers, 1)
|
||||
assert.Equal(t, "resources/namespaces/cluster/ns-1.json", tarWriter.headers[0].Name)
|
||||
}
|
||||
|
||||
func TestBackupResourceListAllNamespacesExcludesCorrectly(t *testing.T) {
|
||||
req := &Request{
|
||||
Backup: &v1.Backup{
|
||||
Spec: v1.BackupSpec{
|
||||
LabelSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
NamespaceIncludesExcludes: collections.NewIncludesExcludes().Excludes("ns-1"),
|
||||
ResourceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"),
|
||||
}
|
||||
|
||||
backedUpItems := map[itemKey]struct{}{}
|
||||
|
||||
dynamicFactory := &velerotest.FakeDynamicFactory{}
|
||||
defer dynamicFactory.AssertExpectations(t)
|
||||
|
||||
discoveryHelper := velerotest.NewFakeDiscoveryHelper(true, nil)
|
||||
|
||||
cohabitatingResources := map[string]*cohabitatingResource{}
|
||||
|
||||
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
|
||||
defer podCommandExecutor.AssertExpectations(t)
|
||||
|
||||
tarWriter := &fakeTarWriter{}
|
||||
|
||||
rb := (&defaultResourceBackupperFactory{}).newResourceBackupper(
|
||||
velerotest.NewLogger(),
|
||||
req,
|
||||
dynamicFactory,
|
||||
discoveryHelper,
|
||||
backedUpItems,
|
||||
cohabitatingResources,
|
||||
podCommandExecutor,
|
||||
tarWriter,
|
||||
nil, // restic backupper
|
||||
newPVCSnapshotTracker(),
|
||||
nil,
|
||||
).(*defaultResourceBackupper)
|
||||
|
||||
itemBackupperFactory := &mockItemBackupperFactory{}
|
||||
defer itemBackupperFactory.AssertExpectations(t)
|
||||
rb.itemBackupperFactory = itemBackupperFactory
|
||||
|
||||
itemHookHandler := &mockItemHookHandler{}
|
||||
defer itemHookHandler.AssertExpectations(t)
|
||||
|
||||
itemBackupper := &mockItemBackupper{}
|
||||
defer itemBackupper.AssertExpectations(t)
|
||||
|
||||
itemBackupperFactory.On("newItemBackupper",
|
||||
req,
|
||||
backedUpItems,
|
||||
podCommandExecutor,
|
||||
tarWriter,
|
||||
dynamicFactory,
|
||||
discoveryHelper,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
).Return(itemBackupper)
|
||||
|
||||
client := &velerotest.FakeDynamicClient{}
|
||||
defer client.AssertExpectations(t)
|
||||
|
||||
coreV1Group := schema.GroupVersion{Group: "", Version: "v1"}
|
||||
dynamicFactory.On("ClientForGroupVersionResource", coreV1Group, namespacesResource, "").Return(client, nil)
|
||||
|
||||
ns1 := velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-1"}}`)
|
||||
ns2 := velerotest.UnstructuredOrDie(`{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"ns-2"}}`)
|
||||
list := &unstructured.UnstructuredList{
|
||||
Items: []unstructured.Unstructured{*ns1, *ns2},
|
||||
}
|
||||
client.On("List", metav1.ListOptions{LabelSelector: metav1.FormatLabelSelector(req.Backup.Spec.LabelSelector)}).Return(list, nil)
|
||||
|
||||
itemBackupper.On("backupItem", mock.AnythingOfType("*logrus.Entry"), ns2, kuberesource.Namespaces).Return(nil)
|
||||
|
||||
err := rb.backupResource(v1Group, namespacesResource)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
type mockItemBackupperFactory struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (ibf *mockItemBackupperFactory) newItemBackupper(
|
||||
backup *Request,
|
||||
backedUpItems map[itemKey]struct{},
|
||||
podCommandExecutor podexec.PodCommandExecutor,
|
||||
tarWriter tarWriter,
|
||||
dynamicFactory client.DynamicFactory,
|
||||
discoveryHelper discovery.Helper,
|
||||
resticBackupper restic.Backupper,
|
||||
resticSnapshotTracker *pvcSnapshotTracker,
|
||||
volumeSnapshotterGetter VolumeSnapshotterGetter,
|
||||
) ItemBackupper {
|
||||
args := ibf.Called(
|
||||
backup,
|
||||
backedUpItems,
|
||||
podCommandExecutor,
|
||||
tarWriter,
|
||||
dynamicFactory,
|
||||
discoveryHelper,
|
||||
resticBackupper,
|
||||
resticSnapshotTracker,
|
||||
volumeSnapshotterGetter,
|
||||
)
|
||||
return args.Get(0).(ItemBackupper)
|
||||
}
|
||||
164
pkg/builder/backup_builder.go
Normal file
164
pkg/builder/backup_builder.go
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
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 (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
Example usage:
|
||||
|
||||
var backup = builder.ForBackup("velero", "backup-1").
|
||||
ObjectMeta(
|
||||
builder.WithLabels("foo", "bar"),
|
||||
builder.WithClusterName("cluster-1"),
|
||||
).
|
||||
SnapshotVolumes(true).
|
||||
Result()
|
||||
|
||||
*/
|
||||
|
||||
// BackupBuilder builds Backup objects.
|
||||
type BackupBuilder struct {
|
||||
object *velerov1api.Backup
|
||||
}
|
||||
|
||||
// ForBackup is the constructor for a BackupBuilder.
|
||||
func ForBackup(ns, name string) *BackupBuilder {
|
||||
return &BackupBuilder{
|
||||
object: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: velerov1api.SchemeGroupVersion.String(),
|
||||
Kind: "Backup",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built Backup.
|
||||
func (b *BackupBuilder) Result() *velerov1api.Backup {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the Backup's ObjectMeta.
|
||||
func (b *BackupBuilder) ObjectMeta(opts ...ObjectMetaOpt) *BackupBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// IncludedNamespaces sets the Backup's included namespaces.
|
||||
func (b *BackupBuilder) IncludedNamespaces(namespaces ...string) *BackupBuilder {
|
||||
b.object.Spec.IncludedNamespaces = namespaces
|
||||
return b
|
||||
}
|
||||
|
||||
// ExcludedNamespaces sets the Backup's excluded namespaces.
|
||||
func (b *BackupBuilder) ExcludedNamespaces(namespaces ...string) *BackupBuilder {
|
||||
b.object.Spec.ExcludedNamespaces = namespaces
|
||||
return b
|
||||
}
|
||||
|
||||
// IncludedResources sets the Backup's included resources.
|
||||
func (b *BackupBuilder) IncludedResources(resources ...string) *BackupBuilder {
|
||||
b.object.Spec.IncludedResources = resources
|
||||
return b
|
||||
}
|
||||
|
||||
// ExcludedResources sets the Backup's excluded resources.
|
||||
func (b *BackupBuilder) ExcludedResources(resources ...string) *BackupBuilder {
|
||||
b.object.Spec.ExcludedResources = resources
|
||||
return b
|
||||
}
|
||||
|
||||
// IncludeClusterResources sets the Backup's "include cluster resources" flag.
|
||||
func (b *BackupBuilder) IncludeClusterResources(val bool) *BackupBuilder {
|
||||
b.object.Spec.IncludeClusterResources = &val
|
||||
return b
|
||||
}
|
||||
|
||||
// LabelSelector sets the Backup's label selector.
|
||||
func (b *BackupBuilder) LabelSelector(selector *metav1.LabelSelector) *BackupBuilder {
|
||||
b.object.Spec.LabelSelector = selector
|
||||
return b
|
||||
}
|
||||
|
||||
// SnapshotVolumes sets the Backup's "snapshot volumes" flag.
|
||||
func (b *BackupBuilder) SnapshotVolumes(val bool) *BackupBuilder {
|
||||
b.object.Spec.SnapshotVolumes = &val
|
||||
return b
|
||||
}
|
||||
|
||||
// Phase sets the Backup's phase.
|
||||
func (b *BackupBuilder) Phase(phase velerov1api.BackupPhase) *BackupBuilder {
|
||||
b.object.Status.Phase = phase
|
||||
return b
|
||||
}
|
||||
|
||||
// StorageLocation sets the Backup's storage location.
|
||||
func (b *BackupBuilder) StorageLocation(location string) *BackupBuilder {
|
||||
b.object.Spec.StorageLocation = location
|
||||
return b
|
||||
}
|
||||
|
||||
// VolumeSnapshotLocations sets the Backup's volume snapshot locations.
|
||||
func (b *BackupBuilder) VolumeSnapshotLocations(locations ...string) *BackupBuilder {
|
||||
b.object.Spec.VolumeSnapshotLocations = locations
|
||||
return b
|
||||
}
|
||||
|
||||
// TTL sets the Backup's TTL.
|
||||
func (b *BackupBuilder) TTL(ttl time.Duration) *BackupBuilder {
|
||||
b.object.Spec.TTL.Duration = ttl
|
||||
return b
|
||||
}
|
||||
|
||||
// Expiration sets the Backup's expiration.
|
||||
func (b *BackupBuilder) Expiration(val time.Time) *BackupBuilder {
|
||||
b.object.Status.Expiration.Time = val
|
||||
return b
|
||||
}
|
||||
|
||||
// StartTimestamp sets the Backup's start timestamp.
|
||||
func (b *BackupBuilder) StartTimestamp(val time.Time) *BackupBuilder {
|
||||
b.object.Status.StartTimestamp.Time = val
|
||||
return b
|
||||
}
|
||||
|
||||
// NoTypeMeta removes the type meta from the Backup.
|
||||
func (b *BackupBuilder) NoTypeMeta() *BackupBuilder {
|
||||
b.object.TypeMeta = metav1.TypeMeta{}
|
||||
return b
|
||||
}
|
||||
|
||||
// Hooks sets the Backup's hooks.
|
||||
func (b *BackupBuilder) Hooks(hooks velerov1api.BackupHooks) *BackupBuilder {
|
||||
b.object.Spec.Hooks = hooks
|
||||
return b
|
||||
}
|
||||
88
pkg/builder/backup_storage_location_builder.go
Normal file
88
pkg/builder/backup_storage_location_builder.go
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright 2017, 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 (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
// BackupStorageLocationBuilder builds BackupStorageLocation objects.
|
||||
type BackupStorageLocationBuilder struct {
|
||||
object *velerov1api.BackupStorageLocation
|
||||
}
|
||||
|
||||
// ForBackupStorageLocation is the constructor for a BackupStorageLocationBuilder.
|
||||
func ForBackupStorageLocation(ns, name string) *BackupStorageLocationBuilder {
|
||||
return &BackupStorageLocationBuilder{
|
||||
object: &velerov1api.BackupStorageLocation{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: velerov1api.SchemeGroupVersion.String(),
|
||||
Kind: "BackupStorageLocation",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built BackupStorageLocation.
|
||||
func (b *BackupStorageLocationBuilder) Result() *velerov1api.BackupStorageLocation {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the BackupStorageLocation's ObjectMeta.
|
||||
func (b *BackupStorageLocationBuilder) ObjectMeta(opts ...ObjectMetaOpt) *BackupStorageLocationBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Provider sets the BackupStorageLocation's provider.
|
||||
func (b *BackupStorageLocationBuilder) Provider(name string) *BackupStorageLocationBuilder {
|
||||
b.object.Spec.Provider = name
|
||||
return b
|
||||
}
|
||||
|
||||
// Bucket sets the BackupStorageLocation's object storage bucket.
|
||||
func (b *BackupStorageLocationBuilder) Bucket(val string) *BackupStorageLocationBuilder {
|
||||
if b.object.Spec.StorageType.ObjectStorage == nil {
|
||||
b.object.Spec.StorageType.ObjectStorage = new(velerov1api.ObjectStorageLocation)
|
||||
}
|
||||
b.object.Spec.ObjectStorage.Bucket = val
|
||||
return b
|
||||
}
|
||||
|
||||
// Prefix sets the BackupStorageLocation's object storage prefix.
|
||||
func (b *BackupStorageLocationBuilder) Prefix(val string) *BackupStorageLocationBuilder {
|
||||
if b.object.Spec.StorageType.ObjectStorage == nil {
|
||||
b.object.Spec.StorageType.ObjectStorage = new(velerov1api.ObjectStorageLocation)
|
||||
}
|
||||
b.object.Spec.ObjectStorage.Prefix = val
|
||||
return b
|
||||
}
|
||||
|
||||
// AccessMode sets the BackupStorageLocation's access mode.
|
||||
func (b *BackupStorageLocationBuilder) AccessMode(accessMode velerov1api.BackupStorageLocationAccessMode) *BackupStorageLocationBuilder {
|
||||
b.object.Spec.AccessMode = accessMode
|
||||
return b
|
||||
}
|
||||
63
pkg/builder/config_map_builder.go
Normal file
63
pkg/builder/config_map_builder.go
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
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 (
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// ConfigMapBuilder builds ConfigMap objects.
|
||||
type ConfigMapBuilder struct {
|
||||
object *corev1api.ConfigMap
|
||||
}
|
||||
|
||||
// ForConfigMap is the constructor for a ConfigMapBuilder.
|
||||
func ForConfigMap(ns, name string) *ConfigMapBuilder {
|
||||
return &ConfigMapBuilder{
|
||||
object: &corev1api.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: corev1api.SchemeGroupVersion.String(),
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built ConfigMap.
|
||||
func (b *ConfigMapBuilder) Result() *corev1api.ConfigMap {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the ConfigMap's ObjectMeta.
|
||||
func (b *ConfigMapBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ConfigMapBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Data set's the ConfigMap's data.
|
||||
func (b *ConfigMapBuilder) Data(vals ...string) *ConfigMapBuilder {
|
||||
b.object.Data = setMapEntries(b.object.Data, vals...)
|
||||
return b
|
||||
}
|
||||
68
pkg/builder/container_builder.go
Normal file
68
pkg/builder/container_builder.go
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
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 (
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// ContainerBuilder builds Container objects
|
||||
type ContainerBuilder struct {
|
||||
object *corev1api.Container
|
||||
}
|
||||
|
||||
// ForContainer is the constructor for ContainerBuilder.
|
||||
func ForContainer(name, image string) *ContainerBuilder {
|
||||
return &ContainerBuilder{
|
||||
object: &corev1api.Container{
|
||||
Name: name,
|
||||
Image: image,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built Container.
|
||||
func (b *ContainerBuilder) Result() *corev1api.Container {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// Args sets the container's Args.
|
||||
func (b *ContainerBuilder) Args(args ...string) *ContainerBuilder {
|
||||
b.object.Args = append(b.object.Args, args...)
|
||||
return b
|
||||
}
|
||||
|
||||
// VolumeMounts sets the container's VolumeMounts.
|
||||
func (b *ContainerBuilder) VolumeMounts(volumeMounts ...*corev1api.VolumeMount) *ContainerBuilder {
|
||||
for _, v := range volumeMounts {
|
||||
b.object.VolumeMounts = append(b.object.VolumeMounts, *v)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Resources sets the container's Resources.
|
||||
func (b *ContainerBuilder) Resources(resources *corev1api.ResourceRequirements) *ContainerBuilder {
|
||||
b.object.Resources = *resources
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *ContainerBuilder) Env(vars ...*corev1api.EnvVar) *ContainerBuilder {
|
||||
for _, v := range vars {
|
||||
b.object.Env = append(b.object.Env, *v)
|
||||
}
|
||||
return b
|
||||
}
|
||||
57
pkg/builder/deployment_builder.go
Normal file
57
pkg/builder/deployment_builder.go
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
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 (
|
||||
appsv1api "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// DeploymentBuilder builds Deployment objects.
|
||||
type DeploymentBuilder struct {
|
||||
object *appsv1api.Deployment
|
||||
}
|
||||
|
||||
// ForDeployment is the constructor for a DeploymentBuilder.
|
||||
func ForDeployment(ns, name string) *DeploymentBuilder {
|
||||
return &DeploymentBuilder{
|
||||
object: &appsv1api.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: appsv1api.SchemeGroupVersion.String(),
|
||||
Kind: "Deployment",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built Deployment.
|
||||
func (b *DeploymentBuilder) Result() *appsv1api.Deployment {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the Deployment's ObjectMeta.
|
||||
func (b *DeploymentBuilder) ObjectMeta(opts ...ObjectMetaOpt) *DeploymentBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
62
pkg/builder/namespace_builder.go
Normal file
62
pkg/builder/namespace_builder.go
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
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 (
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// NamespaceBuilder builds Namespace objects.
|
||||
type NamespaceBuilder struct {
|
||||
object *corev1api.Namespace
|
||||
}
|
||||
|
||||
// ForNamespace is the constructor for a NamespaceBuilder.
|
||||
func ForNamespace(name string) *NamespaceBuilder {
|
||||
return &NamespaceBuilder{
|
||||
object: &corev1api.Namespace{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: corev1api.SchemeGroupVersion.String(),
|
||||
Kind: "Namespace",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built Namespace.
|
||||
func (b *NamespaceBuilder) Result() *corev1api.Namespace {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the Namespace's ObjectMeta.
|
||||
func (b *NamespaceBuilder) ObjectMeta(opts ...ObjectMetaOpt) *NamespaceBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Phase sets the namespace's phase
|
||||
func (b *NamespaceBuilder) Phase(val corev1api.NamespacePhase) *NamespaceBuilder {
|
||||
b.object.Status.Phase = val
|
||||
return b
|
||||
}
|
||||
111
pkg/builder/object_meta.go
Normal file
111
pkg/builder/object_meta.go
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
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 (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// ObjectMetaOpt is a functional option for ObjectMeta.
|
||||
type ObjectMetaOpt func(metav1.Object)
|
||||
|
||||
// WithName is a functional option that applies the specified
|
||||
// name to an object.
|
||||
func WithName(val string) func(obj metav1.Object) {
|
||||
return func(obj metav1.Object) {
|
||||
obj.SetName(val)
|
||||
}
|
||||
}
|
||||
|
||||
// WithLabels is a functional option that applies the specified
|
||||
// label keys/values to an object.
|
||||
func WithLabels(vals ...string) func(obj metav1.Object) {
|
||||
return func(obj metav1.Object) {
|
||||
obj.SetLabels(setMapEntries(obj.GetLabels(), vals...))
|
||||
}
|
||||
}
|
||||
|
||||
// WithAnnotations is a functional option that applies the specified
|
||||
// annotation keys/values to an object.
|
||||
func WithAnnotations(vals ...string) func(obj metav1.Object) {
|
||||
return func(obj metav1.Object) {
|
||||
obj.SetAnnotations(setMapEntries(obj.GetAnnotations(), vals...))
|
||||
}
|
||||
}
|
||||
|
||||
func setMapEntries(m map[string]string, vals ...string) map[string]string {
|
||||
if m == nil {
|
||||
m = make(map[string]string)
|
||||
}
|
||||
|
||||
// if we don't have a value for every key, add an empty
|
||||
// string at the end to serve as the value for the last
|
||||
// key.
|
||||
if len(vals)%2 != 0 {
|
||||
vals = append(vals, "")
|
||||
}
|
||||
|
||||
for i := 0; i < len(vals); i += 2 {
|
||||
key := vals[i]
|
||||
val := vals[i+1]
|
||||
|
||||
m[key] = val
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// WithClusterName is a functional option that applies the specified
|
||||
// cluster name to an object.
|
||||
func WithClusterName(val string) func(obj metav1.Object) {
|
||||
return func(obj metav1.Object) {
|
||||
obj.SetClusterName(val)
|
||||
}
|
||||
}
|
||||
|
||||
// WithFinalizers is a functional option that applies the specified
|
||||
// finalizers to an object.
|
||||
func WithFinalizers(vals ...string) func(obj metav1.Object) {
|
||||
return func(obj metav1.Object) {
|
||||
obj.SetFinalizers(vals)
|
||||
}
|
||||
}
|
||||
|
||||
// WithDeletionTimestamp is a functional option that applies the specified
|
||||
// deletion timestamp to an object.
|
||||
func WithDeletionTimestamp(val time.Time) func(obj metav1.Object) {
|
||||
return func(obj metav1.Object) {
|
||||
obj.SetDeletionTimestamp(&metav1.Time{Time: val})
|
||||
}
|
||||
}
|
||||
|
||||
// WithUID is a functional option that applies the specified UID to an object.
|
||||
func WithUID(val string) func(obj metav1.Object) {
|
||||
return func(obj metav1.Object) {
|
||||
obj.SetUID(types.UID(val))
|
||||
}
|
||||
}
|
||||
|
||||
// WithGenerateName is a functional option that applies the specified generate name to an object.
|
||||
func WithGenerateName(val string) func(obj metav1.Object) {
|
||||
return func(obj metav1.Object) {
|
||||
obj.SetGenerateName(val)
|
||||
}
|
||||
}
|
||||
96
pkg/builder/persistent_volume_builder.go
Normal file
96
pkg/builder/persistent_volume_builder.go
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
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 (
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// PersistentVolumeBuilder builds PersistentVolume objects.
|
||||
type PersistentVolumeBuilder struct {
|
||||
object *corev1api.PersistentVolume
|
||||
}
|
||||
|
||||
// ForPersistentVolume is the constructor for a PersistentVolumeBuilder.
|
||||
func ForPersistentVolume(name string) *PersistentVolumeBuilder {
|
||||
return &PersistentVolumeBuilder{
|
||||
object: &corev1api.PersistentVolume{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: corev1api.SchemeGroupVersion.String(),
|
||||
Kind: "PersistentVolume",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built PersistentVolume.
|
||||
func (b *PersistentVolumeBuilder) Result() *corev1api.PersistentVolume {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the PersistentVolume's ObjectMeta.
|
||||
func (b *PersistentVolumeBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PersistentVolumeBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// ReclaimPolicy sets the PersistentVolume's reclaim policy.
|
||||
func (b *PersistentVolumeBuilder) ReclaimPolicy(policy corev1api.PersistentVolumeReclaimPolicy) *PersistentVolumeBuilder {
|
||||
b.object.Spec.PersistentVolumeReclaimPolicy = policy
|
||||
return b
|
||||
}
|
||||
|
||||
// ClaimRef sets the PersistentVolume's claim ref.
|
||||
func (b *PersistentVolumeBuilder) ClaimRef(ns, name string) *PersistentVolumeBuilder {
|
||||
b.object.Spec.ClaimRef = &corev1api.ObjectReference{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// AWSEBSVolumeID sets the PersistentVolume's AWSElasticBlockStore volume ID.
|
||||
func (b *PersistentVolumeBuilder) AWSEBSVolumeID(volumeID string) *PersistentVolumeBuilder {
|
||||
if b.object.Spec.AWSElasticBlockStore == nil {
|
||||
b.object.Spec.AWSElasticBlockStore = new(corev1api.AWSElasticBlockStoreVolumeSource)
|
||||
}
|
||||
b.object.Spec.AWSElasticBlockStore.VolumeID = volumeID
|
||||
return b
|
||||
}
|
||||
|
||||
// CSI sets the PersistentVolume's CSI.
|
||||
func (b *PersistentVolumeBuilder) CSI(driver, volumeHandle string) *PersistentVolumeBuilder {
|
||||
if b.object.Spec.CSI == nil {
|
||||
b.object.Spec.CSI = new(corev1api.CSIPersistentVolumeSource)
|
||||
}
|
||||
b.object.Spec.CSI.Driver = driver
|
||||
b.object.Spec.CSI.VolumeHandle = volumeHandle
|
||||
return b
|
||||
}
|
||||
|
||||
// StorageClass sets the PersistentVolume's storage class name.
|
||||
func (b *PersistentVolumeBuilder) StorageClass(name string) *PersistentVolumeBuilder {
|
||||
b.object.Spec.StorageClassName = name
|
||||
return b
|
||||
}
|
||||
69
pkg/builder/persistent_volume_claim_builder.go
Normal file
69
pkg/builder/persistent_volume_claim_builder.go
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
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 (
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// PersistentVolumeClaimBuilder builds PersistentVolumeClaim objects.
|
||||
type PersistentVolumeClaimBuilder struct {
|
||||
object *corev1api.PersistentVolumeClaim
|
||||
}
|
||||
|
||||
// ForPersistentVolumeClaim is the constructor for a PersistentVolumeClaimBuilder.
|
||||
func ForPersistentVolumeClaim(ns, name string) *PersistentVolumeClaimBuilder {
|
||||
return &PersistentVolumeClaimBuilder{
|
||||
object: &corev1api.PersistentVolumeClaim{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: corev1api.SchemeGroupVersion.String(),
|
||||
Kind: "PersistentVolumeClaim",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built PersistentVolumeClaim.
|
||||
func (b *PersistentVolumeClaimBuilder) Result() *corev1api.PersistentVolumeClaim {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the PersistentVolumeClaim's ObjectMeta.
|
||||
func (b *PersistentVolumeClaimBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PersistentVolumeClaimBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// VolumeName sets the PersistentVolumeClaim's volume name.
|
||||
func (b *PersistentVolumeClaimBuilder) VolumeName(name string) *PersistentVolumeClaimBuilder {
|
||||
b.object.Spec.VolumeName = name
|
||||
return b
|
||||
}
|
||||
|
||||
// StorageClass sets the PersistentVolumeClaim's storage class name.
|
||||
func (b *PersistentVolumeClaimBuilder) StorageClass(name string) *PersistentVolumeClaimBuilder {
|
||||
b.object.Spec.StorageClassName = &name
|
||||
return b
|
||||
}
|
||||
78
pkg/builder/pod_builder.go
Normal file
78
pkg/builder/pod_builder.go
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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 (
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// PodBuilder builds Pod objects.
|
||||
type PodBuilder struct {
|
||||
object *corev1api.Pod
|
||||
}
|
||||
|
||||
// ForPod is the constructor for a PodBuilder.
|
||||
func ForPod(ns, name string) *PodBuilder {
|
||||
return &PodBuilder{
|
||||
object: &corev1api.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: corev1api.SchemeGroupVersion.String(),
|
||||
Kind: "Pod",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built Pod.
|
||||
func (b *PodBuilder) Result() *corev1api.Pod {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the Pod's ObjectMeta.
|
||||
func (b *PodBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PodBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Volumes appends to the pod's volumes
|
||||
func (b *PodBuilder) Volumes(volumes ...*corev1api.Volume) *PodBuilder {
|
||||
for _, v := range volumes {
|
||||
b.object.Spec.Volumes = append(b.object.Spec.Volumes, *v)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// NodeName sets the pod's node name
|
||||
func (b *PodBuilder) NodeName(val string) *PodBuilder {
|
||||
b.object.Spec.NodeName = val
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *PodBuilder) InitContainers(containers ...*corev1api.Container) *PodBuilder {
|
||||
for _, c := range containers {
|
||||
b.object.Spec.InitContainers = append(b.object.Spec.InitContainers, *c)
|
||||
}
|
||||
return b
|
||||
}
|
||||
64
pkg/builder/pod_volume_backup_builder.go
Normal file
64
pkg/builder/pod_volume_backup_builder.go
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
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 (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
// PodVolumeBackupBuilder builds PodVolumeBackup objects
|
||||
type PodVolumeBackupBuilder struct {
|
||||
object *velerov1api.PodVolumeBackup
|
||||
}
|
||||
|
||||
// ForPodVolumeBackup is the constructor for a PodVolumeBackupBuilder.
|
||||
func ForPodVolumeBackup(ns, name string) *PodVolumeBackupBuilder {
|
||||
return &PodVolumeBackupBuilder{
|
||||
object: &velerov1api.PodVolumeBackup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: velerov1api.SchemeGroupVersion.String(),
|
||||
Kind: "PodVolumeBackup",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built PodVolumeBackup.
|
||||
func (b *PodVolumeBackupBuilder) Result() *velerov1api.PodVolumeBackup {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the PodVolumeBackup's ObjectMeta.
|
||||
func (b *PodVolumeBackupBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PodVolumeBackupBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Phase sets the PodVolumeBackup's phase.
|
||||
func (b *PodVolumeBackupBuilder) Phase(phase velerov1api.PodVolumeBackupPhase) *PodVolumeBackupBuilder {
|
||||
b.object.Status.Phase = phase
|
||||
return b
|
||||
}
|
||||
135
pkg/builder/restore_builder.go
Normal file
135
pkg/builder/restore_builder.go
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
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 (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
// RestoreBuilder builds Restore objects.
|
||||
type RestoreBuilder struct {
|
||||
object *velerov1api.Restore
|
||||
}
|
||||
|
||||
// ForRestore is the constructor for a RestoreBuilder.
|
||||
func ForRestore(ns, name string) *RestoreBuilder {
|
||||
return &RestoreBuilder{
|
||||
object: &velerov1api.Restore{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: velerov1api.SchemeGroupVersion.String(),
|
||||
Kind: "Restore",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built Restore.
|
||||
func (b *RestoreBuilder) Result() *velerov1api.Restore {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the Restore's ObjectMeta.
|
||||
func (b *RestoreBuilder) ObjectMeta(opts ...ObjectMetaOpt) *RestoreBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Backup sets the Restore's backup name.
|
||||
func (b *RestoreBuilder) Backup(name string) *RestoreBuilder {
|
||||
b.object.Spec.BackupName = name
|
||||
return b
|
||||
}
|
||||
|
||||
// Schedule sets the Restore's schedule name.
|
||||
func (b *RestoreBuilder) Schedule(name string) *RestoreBuilder {
|
||||
b.object.Spec.ScheduleName = name
|
||||
return b
|
||||
}
|
||||
|
||||
// IncludedNamespaces appends to the Restore's included namespaces.
|
||||
func (b *RestoreBuilder) IncludedNamespaces(namespaces ...string) *RestoreBuilder {
|
||||
b.object.Spec.IncludedNamespaces = append(b.object.Spec.IncludedNamespaces, namespaces...)
|
||||
return b
|
||||
}
|
||||
|
||||
// ExcludedNamespaces appends to the Restore's excluded namespaces.
|
||||
func (b *RestoreBuilder) ExcludedNamespaces(namespaces ...string) *RestoreBuilder {
|
||||
b.object.Spec.ExcludedNamespaces = append(b.object.Spec.ExcludedNamespaces, namespaces...)
|
||||
return b
|
||||
}
|
||||
|
||||
// IncludedResources appends to the Restore's included resources.
|
||||
func (b *RestoreBuilder) IncludedResources(resources ...string) *RestoreBuilder {
|
||||
b.object.Spec.IncludedResources = append(b.object.Spec.IncludedResources, resources...)
|
||||
return b
|
||||
}
|
||||
|
||||
// ExcludedResources appends to the Restore's excluded resources.
|
||||
func (b *RestoreBuilder) ExcludedResources(resources ...string) *RestoreBuilder {
|
||||
b.object.Spec.ExcludedResources = append(b.object.Spec.ExcludedResources, resources...)
|
||||
return b
|
||||
}
|
||||
|
||||
// IncludeClusterResources sets the Restore's "include cluster resources" flag.
|
||||
func (b *RestoreBuilder) IncludeClusterResources(val bool) *RestoreBuilder {
|
||||
b.object.Spec.IncludeClusterResources = &val
|
||||
return b
|
||||
}
|
||||
|
||||
// LabelSelector sets the Restore's label selector.
|
||||
func (b *RestoreBuilder) LabelSelector(selector *metav1.LabelSelector) *RestoreBuilder {
|
||||
b.object.Spec.LabelSelector = selector
|
||||
return b
|
||||
}
|
||||
|
||||
// NamespaceMappings sets the Restore's namespace mappings.
|
||||
func (b *RestoreBuilder) NamespaceMappings(mapping ...string) *RestoreBuilder {
|
||||
if b.object.Spec.NamespaceMapping == nil {
|
||||
b.object.Spec.NamespaceMapping = make(map[string]string)
|
||||
}
|
||||
|
||||
if len(mapping)%2 != 0 {
|
||||
panic("mapping must contain an even number of values")
|
||||
}
|
||||
|
||||
for i := 0; i < len(mapping); i += 2 {
|
||||
b.object.Spec.NamespaceMapping[mapping[i]] = mapping[i+1]
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Phase sets the Restore's phase.
|
||||
func (b *RestoreBuilder) Phase(phase velerov1api.RestorePhase) *RestoreBuilder {
|
||||
b.object.Status.Phase = phase
|
||||
return b
|
||||
}
|
||||
|
||||
// RestorePVs sets the Restore's restore PVs.
|
||||
func (b *RestoreBuilder) RestorePVs(val bool) *RestoreBuilder {
|
||||
b.object.Spec.RestorePVs = &val
|
||||
return b
|
||||
}
|
||||
57
pkg/builder/role_builder.go
Normal file
57
pkg/builder/role_builder.go
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
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 (
|
||||
rbacv1api "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// RoleBuilder builds Role objects.
|
||||
type RoleBuilder struct {
|
||||
object *rbacv1api.Role
|
||||
}
|
||||
|
||||
// ForRole is the constructor for a RoleBuilder.
|
||||
func ForRole(ns, name string) *RoleBuilder {
|
||||
return &RoleBuilder{
|
||||
object: &rbacv1api.Role{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: rbacv1api.SchemeGroupVersion.String(),
|
||||
Kind: "Role",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built Role.
|
||||
func (b *RoleBuilder) Result() *rbacv1api.Role {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the Role's ObjectMeta.
|
||||
func (b *RoleBuilder) ObjectMeta(opts ...ObjectMetaOpt) *RoleBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
91
pkg/builder/schedule_builder.go
Normal file
91
pkg/builder/schedule_builder.go
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
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 (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
// ScheduleBuilder builds Schedule objects.
|
||||
type ScheduleBuilder struct {
|
||||
object *velerov1api.Schedule
|
||||
}
|
||||
|
||||
// ForSchedule is the constructor for a ScheduleBuilder.
|
||||
func ForSchedule(ns, name string) *ScheduleBuilder {
|
||||
return &ScheduleBuilder{
|
||||
object: &velerov1api.Schedule{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: velerov1api.SchemeGroupVersion.String(),
|
||||
Kind: "Schedule",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built Schedule.
|
||||
func (b *ScheduleBuilder) Result() *velerov1api.Schedule {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the Schedule's ObjectMeta.
|
||||
func (b *ScheduleBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ScheduleBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Phase sets the Schedule's phase.
|
||||
func (b *ScheduleBuilder) Phase(phase velerov1api.SchedulePhase) *ScheduleBuilder {
|
||||
b.object.Status.Phase = phase
|
||||
return b
|
||||
}
|
||||
|
||||
// ValidationError appends to the Schedule's validation errors.
|
||||
func (b *ScheduleBuilder) ValidationError(err string) *ScheduleBuilder {
|
||||
b.object.Status.ValidationErrors = append(b.object.Status.ValidationErrors, err)
|
||||
return b
|
||||
}
|
||||
|
||||
// CronSchedule sets the Schedule's cron schedule.
|
||||
func (b *ScheduleBuilder) CronSchedule(expression string) *ScheduleBuilder {
|
||||
b.object.Spec.Schedule = expression
|
||||
return b
|
||||
}
|
||||
|
||||
// LastBackupTime sets the Schedule's last backup time.
|
||||
func (b *ScheduleBuilder) LastBackupTime(val string) *ScheduleBuilder {
|
||||
t, _ := time.Parse("2006-01-02 15:04:05", val)
|
||||
b.object.Status.LastBackup.Time = t
|
||||
return b
|
||||
}
|
||||
|
||||
// Template sets the Schedule's template.
|
||||
func (b *ScheduleBuilder) Template(spec velerov1api.BackupSpec) *ScheduleBuilder {
|
||||
b.object.Spec.Template = spec
|
||||
return b
|
||||
}
|
||||
57
pkg/builder/secret_builder.go
Normal file
57
pkg/builder/secret_builder.go
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
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 (
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// SecretBuilder builds Secret objects.
|
||||
type SecretBuilder struct {
|
||||
object *corev1api.Secret
|
||||
}
|
||||
|
||||
// ForSecret is the constructor for a SecretBuilder.
|
||||
func ForSecret(ns, name string) *SecretBuilder {
|
||||
return &SecretBuilder{
|
||||
object: &corev1api.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: corev1api.SchemeGroupVersion.String(),
|
||||
Kind: "Secret",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built Secret.
|
||||
func (b *SecretBuilder) Result() *corev1api.Secret {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the Secret's ObjectMeta.
|
||||
func (b *SecretBuilder) ObjectMeta(opts ...ObjectMetaOpt) *SecretBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
84
pkg/builder/server_status_request_builder.go
Normal file
84
pkg/builder/server_status_request_builder.go
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright 2018 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 (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
// ServerStatusRequestBuilder builds ServerStatusRequest objects.
|
||||
type ServerStatusRequestBuilder struct {
|
||||
object *velerov1api.ServerStatusRequest
|
||||
}
|
||||
|
||||
// ForServerStatusRequest is the constructor for for a ServerStatusRequestBuilder.
|
||||
func ForServerStatusRequest(ns, name string) *ServerStatusRequestBuilder {
|
||||
return &ServerStatusRequestBuilder{
|
||||
object: &velerov1api.ServerStatusRequest{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: velerov1api.SchemeGroupVersion.String(),
|
||||
Kind: "ServerStatusRequest",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built ServerStatusRequest.
|
||||
func (b *ServerStatusRequestBuilder) Result() *velerov1api.ServerStatusRequest {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the ServerStatusRequest's ObjectMeta.
|
||||
func (b *ServerStatusRequestBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ServerStatusRequestBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Phase sets the ServerStatusRequest's phase.
|
||||
func (b *ServerStatusRequestBuilder) Phase(phase velerov1api.ServerStatusRequestPhase) *ServerStatusRequestBuilder {
|
||||
b.object.Status.Phase = phase
|
||||
return b
|
||||
}
|
||||
|
||||
// ProcessedTimestamp sets the ServerStatusRequest's processed timestamp.
|
||||
func (b *ServerStatusRequestBuilder) ProcessedTimestamp(time time.Time) *ServerStatusRequestBuilder {
|
||||
b.object.Status.ProcessedTimestamp.Time = time
|
||||
return b
|
||||
}
|
||||
|
||||
// ServerVersion sets the ServerStatusRequest's server version.
|
||||
func (b *ServerStatusRequestBuilder) ServerVersion(version string) *ServerStatusRequestBuilder {
|
||||
b.object.Status.ServerVersion = version
|
||||
return b
|
||||
}
|
||||
|
||||
// Plugins sets the ServerStatusRequest's plugins.
|
||||
func (b *ServerStatusRequestBuilder) Plugins(plugins []velerov1api.PluginInfo) *ServerStatusRequestBuilder {
|
||||
b.object.Status.Plugins = plugins
|
||||
return b
|
||||
}
|
||||
57
pkg/builder/service_account_builder.go
Normal file
57
pkg/builder/service_account_builder.go
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
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 (
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// ServiceAccountBuilder builds ServiceAccount objects.
|
||||
type ServiceAccountBuilder struct {
|
||||
object *corev1api.ServiceAccount
|
||||
}
|
||||
|
||||
// ForServiceAccount is the constructor for a ServiceAccountBuilder.
|
||||
func ForServiceAccount(ns, name string) *ServiceAccountBuilder {
|
||||
return &ServiceAccountBuilder{
|
||||
object: &corev1api.ServiceAccount{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: corev1api.SchemeGroupVersion.String(),
|
||||
Kind: "ServiceAccount",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built ServiceAccount.
|
||||
func (b *ServiceAccountBuilder) Result() *corev1api.ServiceAccount {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the ServiceAccount's ObjectMeta.
|
||||
func (b *ServiceAccountBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ServiceAccountBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
56
pkg/builder/storage_class_builder.go
Normal file
56
pkg/builder/storage_class_builder.go
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
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 (
|
||||
storagev1api "k8s.io/api/storage/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// StorageClassBuilder builds StorageClass objects.
|
||||
type StorageClassBuilder struct {
|
||||
object *storagev1api.StorageClass
|
||||
}
|
||||
|
||||
// ForStorageClass is the constructor for a StorageClassBuilder.
|
||||
func ForStorageClass(name string) *StorageClassBuilder {
|
||||
return &StorageClassBuilder{
|
||||
object: &storagev1api.StorageClass{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: storagev1api.SchemeGroupVersion.String(),
|
||||
Kind: "StorageClass",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built StorageClass.
|
||||
func (b *StorageClassBuilder) Result() *storagev1api.StorageClass {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the StorageClass's ObjectMeta.
|
||||
func (b *StorageClassBuilder) ObjectMeta(opts ...ObjectMetaOpt) *StorageClassBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
56
pkg/builder/volume_builder.go
Normal file
56
pkg/builder/volume_builder.go
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
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 (
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// VolumeBuilder builds Volume objects.
|
||||
type VolumeBuilder struct {
|
||||
object *corev1api.Volume
|
||||
}
|
||||
|
||||
// ForVolume is the constructor for a VolumeBuilder.
|
||||
func ForVolume(name string) *VolumeBuilder {
|
||||
return &VolumeBuilder{
|
||||
object: &corev1api.Volume{
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built Volume.
|
||||
func (b *VolumeBuilder) Result() *corev1api.Volume {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// PersistentVolumeClaimSource sets the Volume's persistent volume claim source.
|
||||
func (b *VolumeBuilder) PersistentVolumeClaimSource(claimName string) *VolumeBuilder {
|
||||
b.object.PersistentVolumeClaim = &corev1api.PersistentVolumeClaimVolumeSource{
|
||||
ClaimName: claimName,
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// CSISource sets the Volume's CSI source.
|
||||
func (b *VolumeBuilder) CSISource(driver string) *VolumeBuilder {
|
||||
b.object.CSI = &corev1api.CSIVolumeSource{
|
||||
Driver: driver,
|
||||
}
|
||||
return b
|
||||
}
|
||||
41
pkg/builder/volume_mount_builder.go
Normal file
41
pkg/builder/volume_mount_builder.go
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
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 (
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// VolumeMountBuilder builds VolumeMount objects.
|
||||
type VolumeMountBuilder struct {
|
||||
object *corev1api.VolumeMount
|
||||
}
|
||||
|
||||
// ForVolumeMount is the constructor for a VolumeMountBuilder.
|
||||
func ForVolumeMount(name, mountPath string) *VolumeMountBuilder {
|
||||
return &VolumeMountBuilder{
|
||||
object: &corev1api.VolumeMount{
|
||||
Name: name,
|
||||
MountPath: mountPath,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built VolumeMount.
|
||||
func (b *VolumeMountBuilder) Result() *corev1api.VolumeMount {
|
||||
return b.object
|
||||
}
|
||||
64
pkg/builder/volume_snapshot_location_builder.go
Normal file
64
pkg/builder/volume_snapshot_location_builder.go
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
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 (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
// VolumeSnapshotLocationBuilder builds VolumeSnapshotLocation objects.
|
||||
type VolumeSnapshotLocationBuilder struct {
|
||||
object *velerov1api.VolumeSnapshotLocation
|
||||
}
|
||||
|
||||
// ForVolumeSnapshotLocation is the constructor for a VolumeSnapshotLocationBuilder.
|
||||
func ForVolumeSnapshotLocation(ns, name string) *VolumeSnapshotLocationBuilder {
|
||||
return &VolumeSnapshotLocationBuilder{
|
||||
object: &velerov1api.VolumeSnapshotLocation{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: velerov1api.SchemeGroupVersion.String(),
|
||||
Kind: "VolumeSnapshotLocation",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Result returns the built VolumeSnapshotLocation.
|
||||
func (b *VolumeSnapshotLocationBuilder) Result() *velerov1api.VolumeSnapshotLocation {
|
||||
return b.object
|
||||
}
|
||||
|
||||
// ObjectMeta applies functional options to the VolumeSnapshotLocation's ObjectMeta.
|
||||
func (b *VolumeSnapshotLocationBuilder) ObjectMeta(opts ...ObjectMetaOpt) *VolumeSnapshotLocationBuilder {
|
||||
for _, opt := range opts {
|
||||
opt(b.object)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Provider sets the VolumeSnapshotLocation's provider.
|
||||
func (b *VolumeSnapshotLocationBuilder) Provider(name string) *VolumeSnapshotLocationBuilder {
|
||||
b.object.Spec.Provider = name
|
||||
return b
|
||||
}
|
||||
@@ -114,5 +114,5 @@ func (d *dynamicResourceClient) Get(name string, opts metav1.GetOptions) (*unstr
|
||||
}
|
||||
|
||||
func (d *dynamicResourceClient) Patch(name string, data []byte) (*unstructured.Unstructured, error) {
|
||||
return d.resourceClient.Patch(name, types.MergePatchType, data, metav1.UpdateOptions{})
|
||||
return d.resourceClient.Patch(name, types.MergePatchType, data, metav1.PatchOptions{})
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
v1 "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
@@ -38,6 +39,9 @@ type Factory interface {
|
||||
// KubeClient returns a Kubernetes client. It uses the following priority to specify the cluster
|
||||
// configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration.
|
||||
KubeClient() (kubernetes.Interface, error)
|
||||
// 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)
|
||||
Namespace() string
|
||||
}
|
||||
|
||||
@@ -103,6 +107,19 @@ func (f *factory) KubeClient() (kubernetes.Interface, error) {
|
||||
return kubeClient, nil
|
||||
}
|
||||
|
||||
func (f *factory) DynamicClient() (dynamic.Interface, error) {
|
||||
clientConfig, err := Config(f.kubeconfig, f.kubecontext, f.baseName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dynamicClient, err := dynamic.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
return dynamicClient, nil
|
||||
}
|
||||
|
||||
func (f *factory) Namespace() string {
|
||||
return f.namespace
|
||||
}
|
||||
|
||||
@@ -36,12 +36,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
s3URLKey = "s3Url"
|
||||
publicURLKey = "publicUrl"
|
||||
kmsKeyIDKey = "kmsKeyId"
|
||||
s3ForcePathStyleKey = "s3ForcePathStyle"
|
||||
bucketKey = "bucket"
|
||||
signatureVersionKey = "signatureVersion"
|
||||
s3URLKey = "s3Url"
|
||||
publicURLKey = "publicUrl"
|
||||
kmsKeyIDKey = "kmsKeyId"
|
||||
s3ForcePathStyleKey = "s3ForcePathStyle"
|
||||
bucketKey = "bucket"
|
||||
signatureVersionKey = "signatureVersion"
|
||||
credentialProfileKey = "profile"
|
||||
)
|
||||
|
||||
type s3Interface interface {
|
||||
@@ -81,6 +82,7 @@ func (o *ObjectStore) Init(config map[string]string) error {
|
||||
kmsKeyIDKey,
|
||||
s3ForcePathStyleKey,
|
||||
signatureVersionKey,
|
||||
credentialProfileKey,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -92,6 +94,7 @@ func (o *ObjectStore) Init(config map[string]string) error {
|
||||
kmsKeyID = config[kmsKeyIDKey]
|
||||
s3ForcePathStyleVal = config[s3ForcePathStyleKey]
|
||||
signatureVersion = config[signatureVersionKey]
|
||||
credentialProfile = config[credentialProfileKey]
|
||||
|
||||
// note that bucket is automatically added to the config map
|
||||
// by the server from the ObjectStorageProviderConfig so
|
||||
@@ -124,7 +127,7 @@ func (o *ObjectStore) Init(config map[string]string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
serverSession, err := getSession(serverConfig)
|
||||
serverSession, err := getSession(serverConfig, credentialProfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -145,7 +148,7 @@ func (o *ObjectStore) Init(config map[string]string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
publicSession, err := getSession(publicConfig)
|
||||
publicSession, err := getSession(publicConfig, credentialProfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -48,8 +48,10 @@ type VolumeSnapshotter struct {
|
||||
ec2 *ec2.EC2
|
||||
}
|
||||
|
||||
func getSession(config *aws.Config) (*session.Session, error) {
|
||||
sess, err := session.NewSession(config)
|
||||
// takes AWS credential config & a profile to create a new session
|
||||
func getSession(config *aws.Config, profile string) (*session.Session, error) {
|
||||
sessionOptions := session.Options{Config: *config, Profile: profile}
|
||||
sess, err := session.NewSessionWithOptions(sessionOptions)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
@@ -66,18 +68,19 @@ func NewVolumeSnapshotter(logger logrus.FieldLogger) *VolumeSnapshotter {
|
||||
}
|
||||
|
||||
func (b *VolumeSnapshotter) Init(config map[string]string) error {
|
||||
if err := cloudprovider.ValidateVolumeSnapshotterConfigKeys(config, regionKey); err != nil {
|
||||
if err := cloudprovider.ValidateVolumeSnapshotterConfigKeys(config, regionKey, credentialProfileKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
region := config[regionKey]
|
||||
credentialProfile := config[credentialProfileKey]
|
||||
if region == "" {
|
||||
return errors.Errorf("missing %s in aws configuration", regionKey)
|
||||
}
|
||||
|
||||
awsConfig := aws.NewConfig().WithRegion(region)
|
||||
|
||||
sess, err := getSession(awsConfig)
|
||||
sess, err := getSession(awsConfig, credentialProfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user