mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-11 23:32:53 +00:00
Compare commits
102 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87d86a45a6 | ||
|
|
6e20eaaba8 | ||
|
|
afa552ca67 | ||
|
|
6f374b5709 | ||
|
|
062a598d8e | ||
|
|
cfc4651078 | ||
|
|
d85d785cf0 | ||
|
|
5caa97f335 | ||
|
|
6d729a90ca | ||
|
|
e53369d509 | ||
|
|
b60e6ff21e | ||
|
|
543678140b | ||
|
|
696117365f | ||
|
|
44306e537e | ||
|
|
cd31141b93 | ||
|
|
0547b1d945 | ||
|
|
a179ae01ca | ||
|
|
be1cd03023 | ||
|
|
b5edac3c83 | ||
|
|
debcbd2f8e | ||
|
|
c952932f1b | ||
|
|
aed504a0fd | ||
|
|
1513674548 | ||
|
|
9764845530 | ||
|
|
1dcaa1bf75 | ||
|
|
ac50b457ad | ||
|
|
9cf35d9ba7 | ||
|
|
36a4c28f61 | ||
|
|
474de24d48 | ||
|
|
648582c85e | ||
|
|
839a9646aa | ||
|
|
7369e4d99e | ||
|
|
03bd6c85c8 | ||
|
|
f5d10c5474 | ||
|
|
20ac603747 | ||
|
|
45168087f1 | ||
|
|
97c14e34be | ||
|
|
718a94ad05 | ||
|
|
89d4e4417f | ||
|
|
71fd7cc5a7 | ||
|
|
d33982b811 | ||
|
|
e0098d8a69 | ||
|
|
e9ece0f7b5 | ||
|
|
d1a1d063e1 | ||
|
|
e6eb5372ea | ||
|
|
62b2a0e17f | ||
|
|
9ed43f96c1 | ||
|
|
52b6838004 | ||
|
|
5d2c9e2ba1 | ||
|
|
3a4e441af8 | ||
|
|
c663ce15ab | ||
|
|
681123596f | ||
|
|
14170b52a8 | ||
|
|
dd2d040fcf | ||
|
|
d4bbd7b817 | ||
|
|
827d5d34f5 | ||
|
|
98a09bcbc5 | ||
|
|
9eca0fcbff | ||
|
|
70e9391278 | ||
|
|
d7d6a85e46 | ||
|
|
7914138dd7 | ||
|
|
e391e43192 | ||
|
|
5b28d70e49 | ||
|
|
a68e5fc330 | ||
|
|
5d6da6517b | ||
|
|
9b9bb62968 | ||
|
|
4364a813c1 | ||
|
|
6edf279bd8 | ||
|
|
1386a85657 | ||
|
|
0b87fbbde8 | ||
|
|
4e2f4bb6af | ||
|
|
0e8a7a23cb | ||
|
|
19e65689ef | ||
|
|
6ac0398c7b | ||
|
|
db139cf07c | ||
|
|
4e05e81ca2 | ||
|
|
c5a2137538 | ||
|
|
1fdb647c7f | ||
|
|
9de644f1a4 | ||
|
|
5bafa9b317 | ||
|
|
fcf7f27967 | ||
|
|
028818a053 | ||
|
|
94872ea2fc | ||
|
|
8e672408a2 | ||
|
|
7005879f3f | ||
|
|
bc25e789e0 | ||
|
|
cffb639380 | ||
|
|
9011b192e9 | ||
|
|
bbcbde084d | ||
|
|
e83ec79df3 | ||
|
|
2636730ef2 | ||
|
|
86efd1577e | ||
|
|
91ccc4adb2 | ||
|
|
a63a82fcb0 | ||
|
|
d0d143e119 | ||
|
|
c27c3cd56a | ||
|
|
84dbd13313 | ||
|
|
caeb4ae404 | ||
|
|
e60d9f2821 | ||
|
|
01e2dcb364 | ||
|
|
9189cffb1c | ||
|
|
f42c63af1b |
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
||||
.go/
|
||||
.go.std/
|
||||
site/
|
||||
11
.github/auto_assign.yml
vendored
Normal file
11
.github/auto_assign.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
addReviewers: true
|
||||
addAssignees: author
|
||||
|
||||
# Set this to the length of the reviewers list because the default was not including everyone
|
||||
numberOfReviewers: 4
|
||||
|
||||
reviewers:
|
||||
- nrb
|
||||
- ashish-amarnath
|
||||
- carlisia
|
||||
- zubron
|
||||
16
.github/workflows/auto_assign_prs.yml
vendored
Normal file
16
.github/workflows/auto_assign_prs.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: 'Auto Assign PR Reviewers'
|
||||
# pull_request_target means that this will run on pull requests, but in the context of the base repo.
|
||||
# This should mean PRs from forks are supported.
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened, sychronized]
|
||||
|
||||
|
||||
jobs:
|
||||
add-reviews:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: kentaro-m/auto-assign-action@v1.1.1
|
||||
with:
|
||||
configuration-path: ".github/auto_assign.yml"
|
||||
repo-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
20
.github/workflows/crds-verify-k8s-1-16-9.yaml
vendored
Normal file
20
.github/workflows/crds-verify-k8s-1-16-9.yaml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: "Verify Velero CRDs on k8s 1.16.9"
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
kind:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: engineerd/setup-kind@v0.4.0
|
||||
with:
|
||||
image: "kindest/node:v1.16.9"
|
||||
- name: Testing
|
||||
run: |
|
||||
kubectl cluster-info
|
||||
kubectl get pods -n kube-system
|
||||
kubectl version
|
||||
echo "current-context:" $(kubectl config current-context)
|
||||
echo "environment-kubeconfig:" ${KUBECONFIG}
|
||||
make local
|
||||
./_output/bin/linux/amd64/velero install --crds-only --dry-run -oyaml | kubectl apply -f -
|
||||
20
.github/workflows/crds-verify-k8s-1-17-0.yaml
vendored
Normal file
20
.github/workflows/crds-verify-k8s-1-17-0.yaml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: "Verify Velero CRDs on k8s 1.17"
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
kind:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: engineerd/setup-kind@v0.4.0
|
||||
with:
|
||||
image: "kindest/node:v1.17.0"
|
||||
- name: Testing
|
||||
run: |
|
||||
kubectl cluster-info
|
||||
kubectl get pods -n kube-system
|
||||
kubectl version
|
||||
echo "current-context:" $(kubectl config current-context)
|
||||
echo "environment-kubeconfig:" ${KUBECONFIG}
|
||||
make local
|
||||
./_output/bin/linux/amd64/velero install --crds-only --dry-run -oyaml | kubectl apply -f -
|
||||
20
.github/workflows/crds-verify-k8s-1-18-4.yaml
vendored
Normal file
20
.github/workflows/crds-verify-k8s-1-18-4.yaml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: "Verify Velero CRDs on k8s 1.18.4"
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
kind:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: engineerd/setup-kind@v0.4.0
|
||||
with:
|
||||
image: "kindest/node:v1.18.4"
|
||||
- name: Testing
|
||||
run: |
|
||||
kubectl cluster-info
|
||||
kubectl get pods -n kube-system
|
||||
kubectl version
|
||||
echo "current-context:" $(kubectl config current-context)
|
||||
echo "environment-kubeconfig:" ${KUBECONFIG}
|
||||
make local
|
||||
./_output/bin/linux/amd64/velero install --crds-only --dry-run -oyaml | kubectl apply -f -
|
||||
4
.github/workflows/push-builder.yml
vendored
4
.github/workflows/push-builder.yml
vendored
@@ -2,7 +2,7 @@ name: build-image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'hack/build-image/Dockerfile'
|
||||
|
||||
@@ -12,6 +12,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- name: Build
|
||||
run: make build-image
|
||||
|
||||
|
||||
11
.github/workflows/push.yml
vendored
11
.github/workflows/push.yml
vendored
@@ -1,8 +1,8 @@
|
||||
name: Master CI
|
||||
name: Main CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ main ]
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
@@ -22,6 +22,13 @@ jobs:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: crazy-max/ghaction-docker-buildx@v3
|
||||
with:
|
||||
buildx-version: latest
|
||||
qemu-version: latest
|
||||
|
||||
- name: Build
|
||||
run: make local
|
||||
|
||||
|
||||
18
.github/workflows/rebase.yml
vendored
Normal file
18
.github/workflows/rebase.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
name: Automatic Rebase
|
||||
jobs:
|
||||
rebase:
|
||||
name: Rebase
|
||||
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Automatic Rebase
|
||||
uses: cirrus-actions/rebase@1.3.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -38,14 +38,8 @@ Tiltfile
|
||||
.vscode
|
||||
*.diff
|
||||
|
||||
# Jekyll compiled data
|
||||
site/_site
|
||||
site/.sass-cache
|
||||
site/.jekyll
|
||||
site/.jekyll-metadata
|
||||
site/.jekyll-cache
|
||||
site/.bundle
|
||||
site/vendor
|
||||
.ruby-version
|
||||
# Hugo compiled data
|
||||
site/public
|
||||
site/resources
|
||||
|
||||
.vs
|
||||
|
||||
37
CHANGELOG.md
37
CHANGELOG.md
@@ -1,10 +1,8 @@
|
||||
## Current release:
|
||||
* [CHANGELOG-1.4.md][14]
|
||||
|
||||
## Development release:
|
||||
* [Unreleased Changes][0]
|
||||
* [CHANGELOG-1.5.md][15]
|
||||
|
||||
## Older releases:
|
||||
* [CHANGELOG-1.4.md][14]
|
||||
* [CHANGELOG-1.3.md][13]
|
||||
* [CHANGELOG-1.2.md][12]
|
||||
* [CHANGELOG-1.1.md][11]
|
||||
@@ -20,18 +18,19 @@
|
||||
* [CHANGELOG-0.3.md][1]
|
||||
|
||||
|
||||
[14]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-1.4.md
|
||||
[13]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-1.3.md
|
||||
[12]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-1.2.md
|
||||
[11]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-1.1.md
|
||||
[10]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-1.0.md
|
||||
[9]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-0.11.md
|
||||
[8]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-0.10.md
|
||||
[7]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-0.9.md
|
||||
[6]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-0.8.md
|
||||
[5]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-0.7.md
|
||||
[4]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-0.6.md
|
||||
[3]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-0.5.md
|
||||
[2]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-0.4.md
|
||||
[1]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/CHANGELOG-0.3.md
|
||||
[0]: https://github.com/vmware-tanzu/velero/blob/master/changelogs/unreleased
|
||||
[15]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.5.md
|
||||
[14]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.4.md
|
||||
[13]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.3.md
|
||||
[12]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.2.md
|
||||
[11]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.1.md
|
||||
[10]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-1.0.md
|
||||
[9]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-0.11.md
|
||||
[8]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-0.10.md
|
||||
[7]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-0.9.md
|
||||
[6]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-0.8.md
|
||||
[5]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-0.7.md
|
||||
[4]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-0.6.md
|
||||
[3]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-0.5.md
|
||||
[2]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-0.4.md
|
||||
[1]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/CHANGELOG-0.3.md
|
||||
[0]: https://github.com/vmware-tanzu/velero/blob/main/changelogs/unreleased
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Contributing
|
||||
|
||||
Authors are expected to follow some guidelines when submitting PRs. Please see [our documentation](https://velero.io/docs/master/code-standards/) for details.
|
||||
Authors are expected to follow some guidelines when submitting PRs. Please see [our documentation](https://velero.io/docs/main/code-standards/) for details.
|
||||
|
||||
61
Dockerfile
Normal file
61
Dockerfile
Normal file
@@ -0,0 +1,61 @@
|
||||
# Copyright 2020 the Velero contributors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
FROM --platform=$BUILDPLATFORM golang:1.14 as builder-env
|
||||
|
||||
ARG GOPROXY
|
||||
ARG PKG
|
||||
ARG VERSION
|
||||
ARG GIT_SHA
|
||||
ARG GIT_TREE_STATE
|
||||
|
||||
ENV CGO_ENABLED=0 \
|
||||
GO111MODULE=on \
|
||||
GOPROXY=${GOPROXY} \
|
||||
LDFLAGS="-X ${PKG}/pkg/buildinfo.Version=${VERSION} -X ${PKG}/pkg/buildinfo.GitSHA=${GIT_SHA} -X ${PKG}/pkg/buildinfo.GitTreeState=${GIT_TREE_STATE}"
|
||||
|
||||
WORKDIR /go/src/github.com/vmware-tanzu/velero
|
||||
|
||||
COPY . /go/src/github.com/vmware-tanzu/velero
|
||||
|
||||
RUN apt-get update && apt-get install -y bzip2
|
||||
|
||||
FROM --platform=$BUILDPLATFORM builder-env as builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG TARGETVARIANT
|
||||
ARG PKG
|
||||
ARG BIN
|
||||
ARG RESTIC_VERSION
|
||||
|
||||
ENV GOOS=${TARGETOS} \
|
||||
GOARCH=${TARGETARCH} \
|
||||
GOARM=${TARGETVARIANT}
|
||||
|
||||
RUN mkdir -p /output/usr/bin && \
|
||||
bash ./hack/download-restic.sh && \
|
||||
export GOARM=$( echo "${GOARM}" | cut -c2-) && \
|
||||
go build -o /output/${BIN} \
|
||||
-ldflags "${LDFLAGS}" ${PKG}/cmd/${BIN}
|
||||
|
||||
FROM ubuntu:focal
|
||||
|
||||
LABEL maintainer="Nolan Brubaker <brubakern@vmware.com>"
|
||||
|
||||
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /output /
|
||||
|
||||
USER nobody:nogroup
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# 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.
|
||||
|
||||
FROM ubuntu:focal
|
||||
|
||||
LABEL maintainer="Nolan Brubaker <brubakern@vmware.com>"
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends ca-certificates wget bzip2 && \
|
||||
wget --quiet https://github.com/restic/restic/releases/download/v0.9.6/restic_0.9.6_linux_amd64.bz2 && \
|
||||
bunzip2 restic_0.9.6_linux_amd64.bz2 && \
|
||||
mv restic_0.9.6_linux_amd64 /usr/bin/restic && \
|
||||
chmod +x /usr/bin/restic && \
|
||||
apt-get remove -y wget bzip2 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
ADD /bin/linux/amd64/velero /velero
|
||||
|
||||
USER nobody:nogroup
|
||||
|
||||
ENTRYPOINT ["/velero"]
|
||||
@@ -1,23 +0,0 @@
|
||||
# Copyright 2020 the Velero contributors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM arm32v7/ubuntu:focal
|
||||
|
||||
ADD /bin/linux/arm/restic /usr/bin/restic
|
||||
|
||||
ADD /bin/linux/arm/velero /velero
|
||||
|
||||
USER nobody:nogroup
|
||||
|
||||
ENTRYPOINT ["/velero"]
|
||||
@@ -1,23 +0,0 @@
|
||||
# Copyright 2020 the Velero contributors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM arm64v8/ubuntu:focal
|
||||
|
||||
ADD /bin/linux/arm64/restic /usr/bin/restic
|
||||
|
||||
ADD /bin/linux/arm64/velero /velero
|
||||
|
||||
USER nobody:nogroup
|
||||
|
||||
ENTRYPOINT ["/velero"]
|
||||
@@ -1,25 +0,0 @@
|
||||
# Copyright 2019 the Velero contributors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM ppc64le/ubuntu:focal
|
||||
|
||||
LABEL maintainer="Prajyot Parab <prajyot.parab@ibm.com>"
|
||||
|
||||
ADD /bin/linux/ppc64le/restic /usr/bin/restic
|
||||
|
||||
ADD /bin/linux/ppc64le/velero /velero
|
||||
|
||||
USER nobody:nogroup
|
||||
|
||||
ENTRYPOINT ["/velero"]
|
||||
@@ -1,23 +0,0 @@
|
||||
# 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.
|
||||
# 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:focal
|
||||
|
||||
LABEL maintainer="Nolan Brubaker <brubakern@vmware.com>"
|
||||
|
||||
ADD /bin/linux/amd64/velero-restic-restore-helper .
|
||||
|
||||
USER nobody:nogroup
|
||||
|
||||
ENTRYPOINT [ "/velero-restic-restore-helper" ]
|
||||
@@ -1,21 +0,0 @@
|
||||
# Copyright 2020 the Velero contributors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM arm32v7/ubuntu:focal
|
||||
|
||||
ADD /bin/linux/arm/velero-restic-restore-helper .
|
||||
|
||||
USER nobody:nogroup
|
||||
|
||||
ENTRYPOINT [ "/velero-restic-restore-helper" ]
|
||||
@@ -1,21 +0,0 @@
|
||||
# Copyright 2020 the Velero contributors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM arm64v8/ubuntu:focal
|
||||
|
||||
ADD /bin/linux/arm64/velero-restic-restore-helper .
|
||||
|
||||
USER nobody:nogroup
|
||||
|
||||
ENTRYPOINT [ "/velero-restic-restore-helper" ]
|
||||
@@ -1,23 +0,0 @@
|
||||
# Copyright 2019 the Velero contributors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM ppc64le/ubuntu:focal
|
||||
|
||||
LABEL maintainer="Prajyot Parab <prajyot.parab@ibm.com>"
|
||||
|
||||
ADD /bin/linux/ppc64le/velero-restic-restore-helper .
|
||||
|
||||
USER nobody:nogroup
|
||||
|
||||
ENTRYPOINT [ "/velero-restic-restore-helper" ]
|
||||
@@ -11,7 +11,7 @@ This document defines the project governance for Velero.
|
||||
The following code repositories are governed by Velero community and maintained under the `vmware-tanzu\Velero` organization.
|
||||
|
||||
* **[Velero](https://github.com/vmware-tanzu/velero):** Main Velero codebase
|
||||
* **[Helm Chart](https://github.com/vmware-tanzu/helm-charts/tree/master/charts/velero):** The Helm chart for the Velero server component
|
||||
* **[Helm Chart](https://github.com/vmware-tanzu/helm-charts/tree/main/charts/velero):** The Helm chart for the Velero server component
|
||||
* **[Velero CSI Plugin](https://github.com/vmware-tanzu/velero-plugin-for-csi):** This repository contains Velero plugins for snapshotting CSI backed PVCs using the CSI beta snapshot APIs
|
||||
* **[Velero Plugin for vSphere](https://github.com/vmware-tanzu/velero-plugin-for-vsphere):** This repository contains the Velero Plugin for vSphere. This plugin is a volume snapshotter plugin that provides crash-consistent snapshots of vSphere block volumes and backup of volume data into S3 compatible storage.
|
||||
* **[Velero Plugin for AWS](https://github.com/vmware-tanzu/velero-plugin-for-aws):** This repository contains the plugins to support running Velero on AWS, including the object store plugin and the volume snapshotter plugin
|
||||
@@ -67,12 +67,12 @@ interested in implementing the proposal should be either deeply engaged in the
|
||||
proposal process or be an author of the proposal.
|
||||
|
||||
The proposal should be documented as a separated markdown file pushed to the root of the
|
||||
`design` folder in the [Velero](https://github.com/vmware-tanzu/velero/tree/master/design)
|
||||
`design` folder in the [Velero](https://github.com/vmware-tanzu/velero/tree/main/design)
|
||||
repository via PR. The name of the file should follow the name pattern `<short
|
||||
meaningful words joined by '-'>_design.md`, e.g:
|
||||
`restore-hooks-design.md`.
|
||||
|
||||
Use the [Proposal Template](https://github.com/vmware-tanzu/velero/blob/master/design/_template.md) as a starting point.
|
||||
Use the [Proposal Template](https://github.com/vmware-tanzu/velero/blob/main/design/_template.md) as a starting point.
|
||||
|
||||
### Proposal Lifecycle
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Velero Maintainers
|
||||
|
||||
[GOVERNANCE.md](https://github.com/vmware-tanzu/velero/blob/master/GOVERNANCE.md) describes governance guidelines and maintainer responsibilities.
|
||||
[GOVERNANCE.md](https://github.com/vmware-tanzu/velero/blob/main/GOVERNANCE.md) describes governance guidelines and maintainer responsibilities.
|
||||
|
||||
## Maintainers
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
| Nolan Brubaker | [nrb](https://github.com/nrb) | [VMware](https://www.github.com/vmware/) |
|
||||
| Ashish Amarnath | [ashish-amarnath](https://github.com/ashish-amarnath) | [VMware](https://www.github.com/vmware/) |
|
||||
| Stephanie Bauman | [stephbman](https://github.com/stephbman) | [VMware](https://www.github.com/vmware/) |
|
||||
| Bridget McErlean | [zubron](https://github.com/zubron) | [VMware](https://www.github.com/vmware/) |
|
||||
|
||||
## Emeritus Maintainers
|
||||
* Adnan Abdulhussein ([prydonius](https://github.com/prydonius))
|
||||
@@ -22,6 +23,6 @@
|
||||
| ----------------------------- | :---------------------: |
|
||||
| Technical Lead | Nolan Brubaker (nrb) |
|
||||
| Kubernetes CSI Liaison | Nolan Brubaker (nrb), Ashish Amarnath (ashish-amarnath) |
|
||||
| Deployment | Carlisia Campos (carlisia), Carlos Tadeu Panato Junior (cpanato) |
|
||||
| Deployment | Carlisia Campos (carlisia), Carlos Tadeu Panato Junior (cpanato), JenTing Hsiao (jenting) |
|
||||
| Community Management | Jonas Rosland (jonasrosland) |
|
||||
| Product Management | Stephanie Bauman (stephbman) |
|
||||
|
||||
214
Makefile
214
Makefile
@@ -23,6 +23,9 @@ PKG := github.com/vmware-tanzu/velero
|
||||
# Where to push the docker image.
|
||||
REGISTRY ?= velero
|
||||
|
||||
# Image name
|
||||
IMAGE ?= $(REGISTRY)/$(BIN)
|
||||
|
||||
# Build image handling. We push a build image for every changed version of
|
||||
# /hack/build-image/Dockerfile. We tag the dockerfile with the short commit hash
|
||||
# of the commit that changed it. When determining if there is a build image in
|
||||
@@ -33,26 +36,49 @@ BUILDER_IMAGE_TAG := $(shell git log -1 --pretty=%h hack/build-image/Dockerfile)
|
||||
BUILDER_IMAGE := $(REGISTRY)/build-image:$(BUILDER_IMAGE_TAG)
|
||||
BUILDER_IMAGE_CACHED := $(shell docker images -q ${BUILDER_IMAGE} 2>/dev/null )
|
||||
|
||||
HUGO_IMAGE := hugo-builder
|
||||
|
||||
# Which architecture to build - see $(ALL_ARCH) for options.
|
||||
# if the 'local' rule is being run, detect the ARCH from 'go env'
|
||||
# if it wasn't specified by the caller.
|
||||
local : ARCH ?= $(shell go env GOOS)-$(shell go env GOARCH)
|
||||
ARCH ?= linux-amd64
|
||||
|
||||
VERSION ?= master
|
||||
VERSION ?= main
|
||||
|
||||
TAG_LATEST ?= false
|
||||
|
||||
ifeq ($(TAG_LATEST), true)
|
||||
IMAGE_TAGS ?= $(IMAGE):$(VERSION) $(IMAGE):latest
|
||||
else
|
||||
IMAGE_TAGS ?= $(IMAGE):$(VERSION)
|
||||
endif
|
||||
|
||||
ifeq ($(shell docker buildx inspect 2>/dev/null | awk '/Status/ { print $$2 }'), running)
|
||||
BUILDX_ENABLED ?= true
|
||||
else
|
||||
BUILDX_ENABLED ?= false
|
||||
endif
|
||||
|
||||
define BUILDX_ERROR
|
||||
buildx not enabled, refusing to run this recipe
|
||||
see: https://velero.io/docs/main/build-from-source/#making-images-and-updating-velero for more info
|
||||
endef
|
||||
|
||||
# The version of restic binary to be downloaded for power architecture
|
||||
RESTIC_VERSION ?= 0.9.6
|
||||
|
||||
CLI_PLATFORMS ?= linux-amd64 linux-arm linux-arm64 darwin-amd64 windows-amd64 linux-ppc64le
|
||||
CONTAINER_PLATFORMS ?= linux-amd64 linux-ppc64le linux-arm linux-arm64
|
||||
MANIFEST_PLATFORMS ?= amd64 ppc64le arm arm64
|
||||
BUILDX_PLATFORMS ?= $(subst -,/,$(ARCH))
|
||||
BUILDX_OUTPUT_TYPE ?= docker
|
||||
|
||||
# set git sha and tree state
|
||||
GIT_SHA = $(shell git rev-parse HEAD)
|
||||
GIT_DIRTY = $(shell git status --porcelain 2> /dev/null)
|
||||
ifneq ($(shell git status --porcelain 2> /dev/null),)
|
||||
GIT_TREE_STATE ?= dirty
|
||||
else
|
||||
GIT_TREE_STATE ?= clean
|
||||
endif
|
||||
|
||||
# The default linters used by lint and local-lint
|
||||
LINTERS ?= "gosec,goconst,gofmt,goimports,unparam"
|
||||
@@ -64,36 +90,7 @@ LINTERS ?= "gosec,goconst,gofmt,goimports,unparam"
|
||||
platform_temp = $(subst -, ,$(ARCH))
|
||||
GOOS = $(word 1, $(platform_temp))
|
||||
GOARCH = $(word 2, $(platform_temp))
|
||||
|
||||
# Set default base image dynamically for each arch
|
||||
ifeq ($(GOARCH),amd64)
|
||||
DOCKERFILE ?= Dockerfile-$(BIN)
|
||||
local-arch:
|
||||
@echo "local environment for amd64 is up-to-date"
|
||||
endif
|
||||
ifeq ($(GOARCH),arm)
|
||||
DOCKERFILE ?= Dockerfile-$(BIN)-arm
|
||||
local-arch:
|
||||
@mkdir -p _output/bin/linux/arm/
|
||||
@wget -q -O - https://github.com/restic/restic/releases/download/v$(RESTIC_VERSION)/restic_$(RESTIC_VERSION)_linux_arm.bz2 | bunzip2 > _output/bin/linux/arm/restic
|
||||
@chmod a+x _output/bin/linux/arm/restic
|
||||
endif
|
||||
ifeq ($(GOARCH),arm64)
|
||||
DOCKERFILE ?= Dockerfile-$(BIN)-arm64
|
||||
local-arch:
|
||||
@mkdir -p _output/bin/linux/arm64/
|
||||
@wget -q -O - https://github.com/restic/restic/releases/download/v$(RESTIC_VERSION)/restic_$(RESTIC_VERSION)_linux_arm64.bz2 | bunzip2 > _output/bin/linux/arm64/restic
|
||||
@chmod a+x _output/bin/linux/arm64/restic
|
||||
endif
|
||||
ifeq ($(GOARCH),ppc64le)
|
||||
DOCKERFILE ?= Dockerfile-$(BIN)-ppc64le
|
||||
local-arch:
|
||||
RESTIC_VERSION=$(RESTIC_VERSION) \
|
||||
./hack/get-restic-ppc64le.sh
|
||||
endif
|
||||
|
||||
MULTIARCH_IMAGE = $(REGISTRY)/$(BIN)
|
||||
IMAGE ?= $(REGISTRY)/$(BIN)-$(GOARCH)
|
||||
GOPROXY ?= https://proxy.golang.org
|
||||
|
||||
# If you want to build all binaries, see the 'all-build' rule.
|
||||
# If you want to build all containers, see the 'all-containers' rule.
|
||||
@@ -106,23 +103,11 @@ build-%:
|
||||
@$(MAKE) --no-print-directory ARCH=$* build
|
||||
@$(MAKE) --no-print-directory ARCH=$* build BIN=velero-restic-restore-helper
|
||||
|
||||
container-%:
|
||||
@$(MAKE) --no-print-directory ARCH=$* container
|
||||
@$(MAKE) --no-print-directory ARCH=$* container BIN=velero-restic-restore-helper
|
||||
|
||||
push-%:
|
||||
@$(MAKE) --no-print-directory ARCH=$* push
|
||||
@$(MAKE) --no-print-directory ARCH=$* push BIN=velero-restic-restore-helper
|
||||
|
||||
all-build: $(addprefix build-, $(CLI_PLATFORMS))
|
||||
|
||||
all-containers: $(addprefix container-, $(CONTAINER_PLATFORMS))
|
||||
|
||||
all-push: $(addprefix push-, $(CONTAINER_PLATFORMS))
|
||||
|
||||
all-manifests:
|
||||
@$(MAKE) manifest
|
||||
@$(MAKE) manifest BIN=velero-restic-restore-helper
|
||||
all-containers: container-builder-env
|
||||
@$(MAKE) --no-print-directory container
|
||||
@$(MAKE) --no-print-directory container BIN=velero-restic-restore-helper
|
||||
|
||||
local: build-dirs
|
||||
GOOS=$(GOOS) \
|
||||
@@ -131,7 +116,7 @@ local: build-dirs
|
||||
PKG=$(PKG) \
|
||||
BIN=$(BIN) \
|
||||
GIT_SHA=$(GIT_SHA) \
|
||||
GIT_DIRTY="$(GIT_DIRTY)" \
|
||||
GIT_TREE_STATE=$(GIT_TREE_STATE) \
|
||||
OUTPUT_DIR=$$(pwd)/_output/bin/$(GOOS)/$(GOARCH) \
|
||||
./hack/build.sh
|
||||
|
||||
@@ -146,13 +131,12 @@ _output/bin/$(GOOS)/$(GOARCH)/$(BIN): build-dirs
|
||||
PKG=$(PKG) \
|
||||
BIN=$(BIN) \
|
||||
GIT_SHA=$(GIT_SHA) \
|
||||
GIT_DIRTY=\"$(GIT_DIRTY)\" \
|
||||
GIT_TREE_STATE=$(GIT_TREE_STATE) \
|
||||
OUTPUT_DIR=/output/$(GOOS)/$(GOARCH) \
|
||||
./hack/build.sh'"
|
||||
|
||||
TTY := $(shell tty -s && echo "-t")
|
||||
|
||||
|
||||
# Example: make shell CMD="date > datefile"
|
||||
shell: build-dirs build-env
|
||||
@# bind-mount the Velero root dir in at /github.com/vmware-tanzu/velero
|
||||
@@ -175,47 +159,36 @@ shell: build-dirs build-env
|
||||
$(BUILDER_IMAGE) \
|
||||
/bin/sh $(CMD)
|
||||
|
||||
DOTFILE_IMAGE = $(subst :,_,$(subst /,_,$(IMAGE))-$(VERSION))
|
||||
container-builder-env:
|
||||
ifneq ($(BUILDX_ENABLED), true)
|
||||
$(error $(BUILDX_ERROR))
|
||||
endif
|
||||
@docker buildx build \
|
||||
--target=builder-env \
|
||||
--build-arg=GOPROXY=$(GOPROXY) \
|
||||
--build-arg=PKG=$(PKG) \
|
||||
--build-arg=VERSION=$(VERSION) \
|
||||
--build-arg=GIT_SHA=$(GIT_SHA) \
|
||||
--build-arg=GIT_TREE_STATE=$(GIT_TREE_STATE) \
|
||||
-f Dockerfile .
|
||||
|
||||
all-containers:
|
||||
$(MAKE) container
|
||||
$(MAKE) container BIN=velero-restic-restore-helper
|
||||
|
||||
container: local-arch .container-$(DOTFILE_IMAGE) container-name
|
||||
.container-$(DOTFILE_IMAGE): _output/bin/$(GOOS)/$(GOARCH)/$(BIN) $(DOCKERFILE)
|
||||
@cp $(DOCKERFILE) _output/.dockerfile-$(BIN)-$(GOOS)-$(GOARCH)
|
||||
@docker build --pull -t $(IMAGE):$(VERSION) -f _output/.dockerfile-$(BIN)-$(GOOS)-$(GOARCH) _output
|
||||
@docker images -q $(IMAGE):$(VERSION) > $@
|
||||
|
||||
container-name:
|
||||
container:
|
||||
ifneq ($(BUILDX_ENABLED), true)
|
||||
$(error $(BUILDX_ERROR))
|
||||
endif
|
||||
@docker buildx build --pull \
|
||||
--output=type=$(BUILDX_OUTPUT_TYPE) \
|
||||
--platform $(BUILDX_PLATFORMS) \
|
||||
$(addprefix -t , $(IMAGE_TAGS)) \
|
||||
--build-arg=PKG=$(PKG) \
|
||||
--build-arg=BIN=$(BIN) \
|
||||
--build-arg=VERSION=$(VERSION) \
|
||||
--build-arg=GIT_SHA=$(GIT_SHA) \
|
||||
--build-arg=GIT_TREE_STATE=$(GIT_TREE_STATE) \
|
||||
--build-arg=RESTIC_VERSION=$(RESTIC_VERSION) \
|
||||
-f Dockerfile .
|
||||
@echo "container: $(IMAGE):$(VERSION)"
|
||||
|
||||
push: .push-$(DOTFILE_IMAGE) push-name
|
||||
.push-$(DOTFILE_IMAGE): .container-$(DOTFILE_IMAGE)
|
||||
@docker push $(IMAGE):$(VERSION)
|
||||
ifeq ($(TAG_LATEST), true)
|
||||
docker tag $(IMAGE):$(VERSION) $(IMAGE):latest
|
||||
docker push $(IMAGE):latest
|
||||
endif
|
||||
@docker images -q $(IMAGE):$(VERSION) > $@
|
||||
|
||||
push-name:
|
||||
@echo "pushed: $(IMAGE):$(VERSION)"
|
||||
|
||||
manifest: .manifest-$(MULTIARCH_IMAGE) manifest-name
|
||||
.manifest-$(MULTIARCH_IMAGE):
|
||||
@DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create $(MULTIARCH_IMAGE):$(VERSION) \
|
||||
$(foreach arch, $(MANIFEST_PLATFORMS), $(MULTIARCH_IMAGE)-$(arch):$(VERSION))
|
||||
@DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push --purge $(MULTIARCH_IMAGE):$(VERSION)
|
||||
ifeq ($(TAG_LATEST), true)
|
||||
@DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create $(MULTIARCH_IMAGE):latest \
|
||||
$(foreach arch, $(MANIFEST_PLATFORMS), $(MULTIARCH_IMAGE)-$(arch):latest)
|
||||
@DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push --purge $(MULTIARCH_IMAGE):latest
|
||||
endif
|
||||
|
||||
manifest-name:
|
||||
@echo "pushed: $(MULTIARCH_IMAGE):$(VERSION)"
|
||||
|
||||
SKIP_TESTS ?=
|
||||
test: build-dirs
|
||||
ifneq ($(SKIP_TESTS), 1)
|
||||
@@ -266,21 +239,25 @@ build-env:
|
||||
ifneq ($(shell git diff --quiet HEAD -- hack/build-image/Dockerfile; echo $$?), 0)
|
||||
@echo "Local changes detected in hack/build-image/Dockerfile"
|
||||
@echo "Preparing a new builder-image"
|
||||
@make build-image
|
||||
$(MAKE) build-image
|
||||
else ifneq ($(BUILDER_IMAGE_CACHED),)
|
||||
@echo "Using Cached Image: $(BUILDER_IMAGE)"
|
||||
else
|
||||
@echo "Trying to pull build-image: $(BUILDER_IMAGE)"
|
||||
docker pull -q $(BUILDER_IMAGE) || make build-image
|
||||
docker pull -q $(BUILDER_IMAGE) || $(MAKE) build-image
|
||||
endif
|
||||
|
||||
build-image:
|
||||
@# When we build a new image we just untag the old one.
|
||||
@# This makes sure we don't leave the orphaned image behind.
|
||||
@id=$$(docker image inspect --format '{{ .ID }}' ${BUILDER_IMAGE} 2>/dev/null); \
|
||||
cd hack/build-image && docker build --pull -t $(BUILDER_IMAGE) . ; \
|
||||
new_id=$$(docker image inspect --format '{{ .ID }}' ${BUILDER_IMAGE} 2>/dev/null); \
|
||||
if [ "$$id" != "" ] && [ "$$id" != "$$new_id" ]; then \
|
||||
$(eval old_id=$(shell docker image inspect --format '{{ .ID }}' ${BUILDER_IMAGE} 2>/dev/null))
|
||||
ifeq ($(BUILDX_ENABLED), true)
|
||||
@cd hack/build-image && docker buildx build --build-arg=GOPROXY=$(GOPROXY) --output=type=docker --pull -t $(BUILDER_IMAGE) .
|
||||
else
|
||||
@cd hack/build-image && docker build --build-arg=GOPROXY=$(GOPROXY) --pull -t $(BUILDER_IMAGE) .
|
||||
endif
|
||||
$(eval new_id=$(shell docker image inspect --format '{{ .ID }}' ${BUILDER_IMAGE} 2>/dev/null))
|
||||
@if [ "$(old_id)" != "" ] && [ "$(old_id)" != "$(new_id)" ]; then \
|
||||
docker rmi -f $$id || true; \
|
||||
fi
|
||||
|
||||
@@ -289,6 +266,9 @@ push-build-image:
|
||||
@# credentials needed to accomplish this.
|
||||
docker push $(BUILDER_IMAGE)
|
||||
|
||||
build-image-hugo:
|
||||
cd site && docker build --pull -t $(HUGO_IMAGE) .
|
||||
|
||||
clean:
|
||||
# if we have a cached image then use it to run go clean --modcache
|
||||
# this test checks if we there is an image id in the BUILDER_IMAGE_CACHED variable.
|
||||
@@ -296,8 +276,8 @@ ifneq ($(strip $(BUILDER_IMAGE_CACHED)),)
|
||||
$(MAKE) shell CMD="-c 'go clean --modcache'"
|
||||
docker rmi -f $(BUILDER_IMAGE) || true
|
||||
endif
|
||||
rm -rf .container-* _output/.dockerfile-* .push-*
|
||||
rm -rf .go _output
|
||||
docker rmi $(HUGO_IMAGE)
|
||||
|
||||
|
||||
.PHONY: modules
|
||||
@@ -338,38 +318,16 @@ release:
|
||||
GITHUB_TOKEN=$(GITHUB_TOKEN) \
|
||||
RELEASE_NOTES_FILE=$(RELEASE_NOTES_FILE) \
|
||||
PUBLISH=$(PUBLISH) \
|
||||
./hack/goreleaser.sh'"
|
||||
./hack/release-tools/goreleaser.sh'"
|
||||
|
||||
serve-docs:
|
||||
serve-docs: build-image-hugo
|
||||
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.4 VELERO_VERSION=v1.4.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.
|
||||
-v "$$(pwd)/site:/srv/hugo" \
|
||||
-it -p 1313:1313 \
|
||||
$(HUGO_IMAGE) \
|
||||
hugo server --bind=0.0.0.0 --enableGitInfo=false
|
||||
# gen-docs generates a new versioned docs directory under site/content/docs.
|
||||
# Please read the documentation in the script for instructions on how to use it.
|
||||
gen-docs:
|
||||
@hack/gen-docs.sh
|
||||
@hack/release-tools/gen-docs.sh
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
![100]
|
||||
|
||||
[![Build Status][1]][2]
|
||||
[![Build Status][1]][2] [](https://bestpractices.coreinfrastructure.org/projects/3811)
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -33,8 +34,8 @@ If you are ready to jump in and test, add code, or help with documentation, foll
|
||||
|
||||
See [the list of releases][6] to find out about feature changes.
|
||||
|
||||
[1]: https://github.com/vmware-tanzu/velero/workflows/Master%20CI/badge.svg
|
||||
[2]: https://github.com/vmware-tanzu/velero/actions?query=workflow%3A"Master+CI"
|
||||
[1]: https://github.com/vmware-tanzu/velero/workflows/Main%20CI/badge.svg
|
||||
[2]: https://github.com/vmware-tanzu/velero/actions?query=workflow%3A"Main+CI"
|
||||
[4]: https://github.com/vmware-tanzu/velero/issues
|
||||
[6]: https://github.com/vmware-tanzu/velero/releases
|
||||
[9]: https://kubernetes.io/docs/setup/
|
||||
@@ -47,4 +48,4 @@ See [the list of releases][6] to find out about feature changes.
|
||||
[29]: https://velero.io/docs/
|
||||
[30]: https://velero.io/docs/troubleshooting
|
||||
[31]: https://velero.io/docs/start-contributing
|
||||
[100]: https://velero.io/docs/master/img/velero.png
|
||||
[100]: https://velero.io/docs/main/img/velero.png
|
||||
|
||||
@@ -7,7 +7,7 @@ This document provides a link to the [Velero Project board](https://app.zenhub.c
|
||||
Discussion on the roadmap can take place in threads under [Issues](https://github.com/vmware-tanzu/velero/issues) or in [community meetings](https://velero.io/community/). Please open and comment on an issue if you want to provide suggestions, use cases, and feedback to an item in the roadmap. Please review the roadmap to avoid potential duplicated effort.
|
||||
|
||||
### How to add an item to the roadmap?
|
||||
One of the most important aspects in any open source community is the concept of proposals. Large changes to the codebase and / or new features should be preceded by a [proposal](https://github.com/vmware-tanzu/velero/blob/master/GOVERNANCE.md#proposal-process) in our repo.
|
||||
One of the most important aspects in any open source community is the concept of proposals. Large changes to the codebase and / or new features should be preceded by a [proposal](https://github.com/vmware-tanzu/velero/blob/main/GOVERNANCE.md#proposal-process) in our repo.
|
||||
For smaller enhancements, you can open an issue to track that initiative or feature request.
|
||||
We work with and rely on community feedback to focus our efforts to improve Velero and maintain a healthy roadmap.
|
||||
|
||||
|
||||
128
SECURITY.md
Normal file
128
SECURITY.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Security Release Process
|
||||
|
||||
Velero is an open source tool with a growing community devoted to safe backup and restore, disaster recovery, and data migration of Kubernetes resources and persistent volumes. The community has adopted this security disclosure and response policy to ensure we responsibly handle critical issues.
|
||||
|
||||
|
||||
## Supported Versions
|
||||
|
||||
The Velero project maintains the following [governance document](https://github.com/vmware-tanzu/velero/blob/main/GOVERNANCE.md), [release document](https://github.com/vmware-tanzu/velero/blob/f42c63af1b9af445e38f78a7256b1c48ef79c10e/site/docs/main/release-instructions.md), and [support document](https://velero.io/docs/main/support-process/). Please refer to these for release and related details. Only the most recent version of Velero is supported. Each [release](https://github.com/vmware-tanzu/velero/releases) includes information about upgrading to the latest version.
|
||||
|
||||
|
||||
## Reporting a Vulnerability - Private Disclosure Process
|
||||
|
||||
Security is of the highest importance and all security vulnerabilities or suspected security vulnerabilities should be reported to Velero privately, to minimize attacks against current users of Velero before they are fixed. Vulnerabilities will be investigated and patched on the next patch (or minor) release as soon as possible. This information could be kept entirely internal to the project.
|
||||
|
||||
If you know of a publicly disclosed security vulnerability for Velero, please **IMMEDIATELY** contact the VMware Security Team (security@vmware.com).
|
||||
|
||||
|
||||
|
||||
**IMPORTANT: Do not file public issues on GitHub for security vulnerabilities**
|
||||
|
||||
To report a vulnerability or a security-related issue, please contact the VMware email address with the details of the vulnerability. The email will be fielded by the VMware Security Team and then shared with the Velero maintainers who have committer and release permissions. Emails will be addressed within 3 business days, including a detailed plan to investigate the issue and any potential workarounds to perform in the meantime. Do not report non-security-impacting bugs through this channel. Use [GitHub issues](https://github.com/vmware-tanzu/velero/issues/new/choose) instead.
|
||||
|
||||
|
||||
## Proposed Email Content
|
||||
|
||||
Provide a descriptive subject line and in the body of the email include the following information:
|
||||
|
||||
|
||||
|
||||
* Basic identity information, such as your name and your affiliation or company.
|
||||
* Detailed steps to reproduce the vulnerability (POC scripts, screenshots, and logs are all helpful to us).
|
||||
* Description of the effects of the vulnerability on Velero and the related hardware and software configurations, so that the VMware Security Team can reproduce it.
|
||||
* How the vulnerability affects Velero usage and an estimation of the attack surface, if there is one.
|
||||
* List other projects or dependencies that were used in conjunction with Velero to produce the vulnerability.
|
||||
|
||||
|
||||
|
||||
|
||||
## When to report a vulnerability
|
||||
|
||||
|
||||
|
||||
* When you think Velero has a potential security vulnerability.
|
||||
* When you suspect a potential vulnerability but you are unsure that it impacts Velero.
|
||||
* When you know of or suspect a potential vulnerability on another project that is used by Velero.
|
||||
|
||||
|
||||
|
||||
|
||||
## Patch, Release, and Disclosure
|
||||
|
||||
The VMware Security Team will respond to vulnerability reports as follows:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1. The Security Team will investigate the vulnerability and determine its effects and criticality.
|
||||
2. If the issue is not deemed to be a vulnerability, the Security Team will follow up with a detailed reason for rejection.
|
||||
3. The Security Team will initiate a conversation with the reporter within 3 business days.
|
||||
4. If a vulnerability is acknowledged and the timeline for a fix is determined, the Security Team will work on a plan to communicate with the appropriate community, including identifying mitigating steps that affected users can take to protect themselves until the fix is rolled out.
|
||||
5. The Security Team will also create a [CVSS](https://www.first.org/cvss/specification-document) using the [CVSS Calculator](https://www.first.org/cvss/calculator/3.0). The Security Team makes the final call on the calculated CVSS; it is better to move quickly than making the CVSS perfect. Issues may also be reported to [Mitre](https://cve.mitre.org/) using this [scoring calculator](https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator). The CVE will initially be set to private.
|
||||
6. The Security Team will work on fixing the vulnerability and perform internal testing before preparing to roll out the fix.
|
||||
7. The Security Team will provide early disclosure of the vulnerability by emailing the [Velero Distributors](https://groups.google.com/u/1/g/projectvelero-distributors) mailing list. Distributors can initially plan for the vulnerability patch ahead of the fix, and later can test the fix and provide feedback to the Velero team. See the section **Early Disclosure to Velero Distributors List** for details about how to join this mailing list.
|
||||
8. A public disclosure date is negotiated by the VMware SecurityTeam, the bug submitter, and the distributors list. We prefer to fully disclose the bug as soon as possible once a user mitigation or patch is available. It is reasonable to delay disclosure when the bug or the fix is not yet fully understood, the solution is not well-tested, or for distributor coordination. The timeframe for disclosure is from immediate (especially if it’s already publicly known) to a few weeks. For a critical vulnerability with a straightforward mitigation, we expect the report date for the public disclosure date to be on the order of 14 business days. The VMware Security Team holds the final say when setting a public disclosure date.
|
||||
9. Once the fix is confirmed, the Security Team will patch the vulnerability in the next patch or minor release, and backport a patch release into all earlier supported releases. Upon release of the patched version of Velero, we will follow the **Public Disclosure Process**.
|
||||
|
||||
|
||||
## Public Disclosure Process
|
||||
|
||||
The Security Team publishes a [public advisory](https://github.com/vmware-tanzu/velero/security/advisories) to the Velero community via GitHub. In most cases, additional communication via Slack, Twitter, mailing lists, blog and other channels will assist in educating Velero users and rolling out the patched release to affected users.
|
||||
|
||||
The Security Team will also publish any mitigating steps users can take until the fix can be applied to their Velero instances. Velero distributors will handle creating and publishing their own security advisories.
|
||||
|
||||
|
||||
|
||||
|
||||
## Mailing lists
|
||||
|
||||
|
||||
|
||||
* Use security@vmware.com to report security concerns to the VMware Security Team, who uses the list to privately discuss security issues and fixes prior to disclosure.
|
||||
* Join the [Velero Distributors](https://groups.google.com/u/1/g/projectvelero-distributors) mailing list for early private information and vulnerability disclosure. Early disclosure may include mitigating steps and additional information on security patch releases. See below for information on how Velero distributors or vendors can apply to join this list.
|
||||
|
||||
|
||||
## Early Disclosure to Velero Distributors List
|
||||
|
||||
The private list is intended to be used primarily to provide actionable information to multiple distributor projects at once. This list is not intended to inform individuals about security issues.
|
||||
|
||||
|
||||
## Membership Criteria
|
||||
|
||||
To be eligible to join the [Velero Distributors](https://groups.google.com/u/1/g/projectvelero-distributors) mailing list, you should:
|
||||
|
||||
|
||||
|
||||
1. Be an active distributor of Velero.
|
||||
2. Have a user base that is not limited to your own organization.
|
||||
3. Have a publicly verifiable track record up to the present day of fixing security issues.
|
||||
4. Not be a downstream or rebuild of another distributor.
|
||||
5. Be a participant and active contributor in the Velero community.
|
||||
6. Accept the Embargo Policy that is outlined below.
|
||||
7. Have someone who is already on the list vouch for the person requesting membership on behalf of your distribution.
|
||||
|
||||
**The terms and conditions of the Embargo Policy apply to all members of this mailing list. A request for membership represents your acceptance to the terms and conditions of the Embargo Policy.**
|
||||
|
||||
|
||||
## Embargo Policy
|
||||
|
||||
The information that members receive on the Velero Distributors mailing list must not be made public, shared, or even hinted at anywhere beyond those who need to know within your specific team, unless you receive explicit approval to do so from the VMware Security Team. This remains true until the public disclosure date/time agreed upon by the list. Members of the list and others cannot use the information for any reason other than to get the issue fixed for your respective distribution's users.
|
||||
|
||||
Before you share any information from the list with members of your team who are required to fix the issue, these team members must agree to the same terms, and only be provided with information on a need-to-know basis.
|
||||
|
||||
In the unfortunate event that you share information beyond what is permitted by this policy, you must urgently inform the VMware Security Team (security@vmware.com) of exactly what information was leaked and to whom. If you continue to leak information and break the policy outlined here, you will be permanently removed from the list.
|
||||
|
||||
|
||||
|
||||
|
||||
## Requesting to Join
|
||||
|
||||
Send new membership requests to projectvelero-distributors@googlegroups.com. In the body of your request please specify how you qualify for membership and fulfill each criterion listed in the Membership Criteria section above.
|
||||
|
||||
|
||||
## Confidentiality, integrity and availability
|
||||
|
||||
We consider vulnerabilities leading to the compromise of data confidentiality, elevation of privilege, or integrity to be our highest priority concerns. Availability, in particular in areas relating to DoS and resource exhaustion, is also a serious security concern. The VMware Security Team takes all vulnerabilities, potential vulnerabilities, and suspected vulnerabilities seriously and will investigate them in an urgent and expeditious manner.
|
||||
|
||||
Note that we do not currently consider the default settings for Velero to be secure-by-default. It is necessary for operators to explicitly configure settings, role based access control, and other resource related features in Velero to provide a hardened Velero environment. We will not act on any security disclosure that relates to a lack of safe defaults. Over time, we will work towards improved safe-by-default configuration, taking into account backwards compatibility.
|
||||
@@ -4,4 +4,4 @@ Thanks for trying out Velero! We welcome all feedback, find all the ways to conn
|
||||
|
||||
- [Velero Community](https://velero.io/community/)
|
||||
|
||||
You can find details on the Velero maintainers' support process [here](https://velero.io/docs/master/support-process/).
|
||||
You can find details on the Velero maintainers' support process [here](https://velero.io/docs/main/support-process/).
|
||||
|
||||
@@ -69,8 +69,8 @@ carefully to ensure a successful upgrade!**
|
||||
- The `Config` CRD has been replaced by `BackupStorageLocation` and `VolumeSnapshotLocation` CRDs.
|
||||
- The interface for external plugins (object/block stores, backup/restore item actions) has changed. If you have authored any custom plugins, they'll
|
||||
need to be updated for v0.10.
|
||||
- The [`ObjectStore.ListCommonPrefixes`](https://github.com/heptio/ark/blob/master/pkg/cloudprovider/object_store.go#L50) signature has changed to add a `prefix` parameter.
|
||||
- Registering plugins has changed. Create a new plugin server with the `NewServer` function, and register plugins with the appropriate functions. See the [`Server`](https://github.com/heptio/ark/blob/master/pkg/plugin/server.go#L37) interface for details.
|
||||
- The [`ObjectStore.ListCommonPrefixes`](https://github.com/vmware-tanzu/velero/blob/main/pkg/cloudprovider/object_store.go#L50) signature has changed to add a `prefix` parameter.
|
||||
- Registering plugins has changed. Create a new plugin server with the `NewServer` function, and register plugins with the appropriate functions. See the [`Server`](https://github.com/vmware-tanzu/velero/blob/main/pkg/plugin/server.go#L37) interface for details.
|
||||
- The organization of Ark data in object storage has changed. Existing data will need to be moved around to conform to the new layout.
|
||||
|
||||
### All Changes
|
||||
@@ -89,7 +89,7 @@ need to be updated for v0.10.
|
||||
- [ec013e6f](https://github.com/heptio/ark/commit/ec013e6f) Document upgrading plugins in the deployment
|
||||
- [d6162e94](https://github.com/heptio/ark/commit/d6162e94) fix goreleaser bugs
|
||||
- [a15df276](https://github.com/heptio/ark/commit/a15df276) Add correct link and change role
|
||||
- [46bed015](https://github.com/heptio/ark/commit/46bed015) add 0.10 breaking changes warning to readme in master
|
||||
- [46bed015](https://github.com/heptio/ark/commit/46bed015) add 0.10 breaking changes warning to readme in main
|
||||
- [e3a7d6a2](https://github.com/heptio/ark/commit/e3a7d6a2) add content for issue 994
|
||||
- [400911e9](https://github.com/heptio/ark/commit/400911e9) address docs issue #978
|
||||
- [b818cc27](https://github.com/heptio/ark/commit/b818cc27) don't require a default provider VSL if there's only 1
|
||||
@@ -247,7 +247,7 @@ need to be updated for v0.10.
|
||||
- [5b89f7b6](https://github.com/heptio/ark/commit/5b89f7b6) Skip backup sync if it already exists in k8s
|
||||
- [c6050845](https://github.com/heptio/ark/commit/c6050845) restore controller: switch to 'c' for receiver name
|
||||
- [706ae07d](https://github.com/heptio/ark/commit/706ae07d) enable a schedule to be provided as the source for a restore
|
||||
- [aea68414](https://github.com/heptio/ark/commit/aea68414) fix up Slack link in troubleshooting on master branch
|
||||
- [aea68414](https://github.com/heptio/ark/commit/aea68414) fix up Slack link in troubleshooting on main branch
|
||||
- [bb8e2e91](https://github.com/heptio/ark/commit/bb8e2e91) Document how to run the Ark server locally
|
||||
- [dc84e591](https://github.com/heptio/ark/commit/dc84e591) Remove outdated namespace deletion content
|
||||
- [23abbc9a](https://github.com/heptio/ark/commit/23abbc9a) fix paths
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
### Highlights:
|
||||
* Ark now has support for backing up and restoring Kubernetes volumes using a free open-source backup tool called [restic](https://github.com/restic/restic).
|
||||
This provides users an out-of-the-box solution for backing up and restoring almost any type of Kubernetes volume, whether or not it has snapshot support
|
||||
integrated with Ark. For more information, see the [documentation](https://github.com/heptio/ark/blob/master/docs/restic.md).
|
||||
integrated with Ark. For more information, see the [documentation](https://github.com/vmware-tanzu/velero/blob/main/docs/restic.md).
|
||||
* Support for Prometheus metrics has been added! View total number of backup attempts (including success or failure), total backup size in bytes, and backup
|
||||
durations. More metrics coming in future releases!
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ We fixed a large number of bugs and made some smaller usability improvements in
|
||||
|
||||
|
||||
### All Changes
|
||||
* Corrected the selfLink for Backup CR in site/docs/master/output-file-format.md (#2292, @RushinthJohn)
|
||||
* Corrected the selfLink for Backup CR in site/docs/main/output-file-format.md (#2292, @RushinthJohn)
|
||||
* Back up schema-less CustomResourceDefinitions as v1beta1, even if they are retrieved via the v1 endpoint. (#2264, @nrb)
|
||||
* Bug fix: restic backup volume snapshot to the second location failed (#2244, @jenting)
|
||||
* Added support of using PV name from volumesnapshotter('SetVolumeID') in case of PV renaming during the restore (#2216, @mynktl)
|
||||
|
||||
@@ -1,3 +1,28 @@
|
||||
## v1.4.2
|
||||
### 2020-07-13
|
||||
|
||||
### Download
|
||||
https://github.com/vmware-tanzu/velero/releases/tag/v1.4.2
|
||||
|
||||
### Container Image
|
||||
`velero/velero:v1.4.2`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.4/
|
||||
|
||||
### Upgrading
|
||||
https://velero.io/docs/v1.4/upgrade-to-1.4/
|
||||
|
||||
### All Changes
|
||||
* log a warning instead of erroring if an additional item returned from a plugin can't be found in the Kubernetes API (#2595, @skriss)
|
||||
* Adjust restic default time out to 4 hours and base pod resource requests to 500m CPU/512Mi memory. (#2696, @nrb)
|
||||
* capture version of the CRD prior before invoking the remap_crd_version backup item action (#2683, @ashish-amarnath)
|
||||
|
||||
|
||||
## v1.4.1
|
||||
|
||||
This tag was created in code, but has no associated docker image due to misconfigured building infrastructure. v1.4.2 fixes this.
|
||||
|
||||
## v1.4.0
|
||||
### 2020-05-26
|
||||
|
||||
|
||||
82
changelogs/CHANGELOG-1.5.md
Normal file
82
changelogs/CHANGELOG-1.5.md
Normal file
@@ -0,0 +1,82 @@
|
||||
## v1.5.1
|
||||
### 2020-09-16
|
||||
|
||||
### Download
|
||||
https://github.com/vmware-tanzu/velero/releases/tag/v1.5.1
|
||||
|
||||
### Container Image
|
||||
`velero/velero:v1.5.1`
|
||||
|
||||
### Documentation
|
||||
https://velero.io/docs/v1.5/
|
||||
|
||||
### Upgrading
|
||||
https://velero.io/docs/v1.5/upgrade-to-1.5/
|
||||
|
||||
### Highlights
|
||||
|
||||
* Auto Volume Backup Using Restic with `--default-volumes-to-restic` flag
|
||||
* DeleteItemAction plugins
|
||||
* Code modernization
|
||||
* Restore Hooks: InitContianer Restore Hooks and Exec Restore Hooks
|
||||
|
||||
### All Changes
|
||||
|
||||
* 🏃♂️ add shortnames for CRDs (#2911, @ashish-amarnath)
|
||||
* Use format version instead of version on `velero backup describe` since version has been deprecated (#2901, @jenting)
|
||||
* fix EnableAPIGroupersions output log format (#2882, @jenting)
|
||||
* Convert ServerStatusRequest controller to kubebuilder (#2838, @carlisia)
|
||||
* rename the PV if VolumeSnapshotter has modified the PV name (#2835, @pawanpraka1)
|
||||
* Implement post-restore exec hooks in pod containers (#2804, @areed)
|
||||
* Check for errors on restic backup command (#2863, @dymurray)
|
||||
* 🐛 fix passing LDFLAGS across build stages (#2853, @ashish-amarnath)
|
||||
* Feature: Invoke DeleteItemAction plugins based on backup contents when a backup is deleted. (#2815, @nrb)
|
||||
* When JSON logging format is enabled, place error message at "error.message" instead of "error" for compatibility with Elasticsearch/ELK and the Elastic Common Schema (#2830, @bgagnon)
|
||||
* discovery Helper support get GroupVersionResource and an APIResource from GroupVersionKind (#2764, @runzexia)
|
||||
* Migrate site from Jekyll to Hugo (#2720, @tbatard)
|
||||
* Add the DeleteItemAction plugin type (#2808, @nrb)
|
||||
* 🐛 Manually patch the generated yaml for restore CRD as a hacky workaround (#2814, @ashish-amarnath)
|
||||
* Setup crd validation github action on k8s versions (#2805, @ashish-amarnath)
|
||||
* 🐛 Supply command to run restic-wait init container (#2802, @ashish-amarnath)
|
||||
* Make init and exec restore hooks as optional in restore hookSpec (#2793, @ashish-amarnath)
|
||||
* Implement restore hooks injecting init containers into pod spec (#2787, @ashish-amarnath)
|
||||
* Pass default-volumes-to-restic flag from create schedule to backup (#2776, @ashish-amarnath)
|
||||
* Enhance Backup to support backing up resources in specific orders and add --ordered-resources option to support this feature. (#2724, @phuong)
|
||||
* Fix inconsistent type for the "resource" structured logging field (#2796, @bgagnon)
|
||||
* Add the ability to set the allowPrivilegeEscalation flag in the securityContext for the Restic restore helper. (#2792, @doughepi)
|
||||
* Add cacert flag for velero backup-location create (#2778, @jenting)
|
||||
* Exclude volumes mounting secrets and configmaps from defaulting volume backups to restic (#2762, @ashish-amarnath)
|
||||
* Add types to implement restore hooks (#2761, @ashish-amarnath)
|
||||
* Add wait group and error channel for restore hooks to restore context. (#2755, @areed)
|
||||
* Refactor image builds to use buildx for multi arch image building (#2754, @robreus)
|
||||
* Add annotation key constants for restore hooks (#2750, @ashish-amarnath)
|
||||
* Adds Start and CompletionTimestamp to RestoreStatus
|
||||
Displays the Timestamps when issued a print or describe (#2748, @thejasbabu)
|
||||
* Move pkg/backup/item_hook_handlers.go to internal/hook (#2734, @nrb)
|
||||
* add metrics for restic back up operation (#2719, @ashish-amarnath)
|
||||
* StorageGrid compatibility by removing explicit gzip accept header setting (#2712, @fvsqr)
|
||||
* restic: add support for setting SecurityContext (runAsUser, runAsGroup) for restore (#2621, @jaygridley)
|
||||
* Add backupValidationFailureTotal to metrics (#2714, @kathpeony)
|
||||
* bump Kubernetes module dependencies to v0.18.4 to fix https://github.com/vmware-tanzu/velero/issues/2540 by adding code compatibility with kubernetes v1.18 (#2651, @laverya)
|
||||
* Add a BSL controller to handle validation + update BSL status phase (validation removed from the server and no longer blocks when there's any invalid BSL) (#2674, @carlisia)
|
||||
* updated acceptable values on cron schedule from 0-7 to 0-6 (#2676, @dthrasher)
|
||||
* Improve velero download doc (#2660, @carlisia)
|
||||
* Update basic-install and release-instructions documentation for Windows Chocolatey package (#2638, @adamrushuk)
|
||||
* move CSI plugin out of prototype into beta (#2636, @ashish-amarnath)
|
||||
* Add a new supported provider for an object storage plugin for Storj (#2635, @jessicagreben)
|
||||
* Update basic-install.md documentation: Add windows cli installation option via chocolatey (#2629, @adamrushuk)
|
||||
* Documentation: Update Jekyll to 4.1.0. Switch from redcarpet to kramdown for Markdown renderer (#2625, @tbatard)
|
||||
* improve builder image handling so that we don't rebuild each `make shell` (#2620, @mauilion)
|
||||
* first check if there are pending changed on the build-image dockerfile if so build it.
|
||||
* then check if there is an image in the registry if so pull it.
|
||||
* then build an image cause we don't have a cached image. (this handles the backward compat case.)
|
||||
* fix make clean to clear go mod cache before removing dirs (for containerized builds)
|
||||
* Add linter checks to Makefile (#2615, @tbatard)
|
||||
* add a CI check for a changelog file (#2613, @ashish-amarnath)
|
||||
* implement option to back up all volumes by default with restic (#2611, @ashish-amarnath)
|
||||
* When a timeout string can't be parsed, log the error as a warning instead of silently consuming the error. (#2610, @nrb)
|
||||
* Azure: support using `aad-pod-identity` auth when using restic (#2602, @skriss)
|
||||
* log a warning instead of erroring if an additional item returned from a plugin can't be found in the Kubernetes API (#2595, @skriss)
|
||||
* when creating new backup from schedule from cli, allow backup name to be automatically generated (#2569, @cblecker)
|
||||
* Convert manifests + BSL api client to kubebuilder (#2561, @carlisia)
|
||||
* backup/restore: reinstantiate backup store just before uploading artifacts to ensure credentials are up-to-date (#2550, @skriss)
|
||||
@@ -1 +0,0 @@
|
||||
backup/restore: reinstantiate backup store just before uploading artifacts to ensure credentials are up-to-date
|
||||
@@ -1 +0,0 @@
|
||||
Convert manifests + BSL api client to kubebuilder
|
||||
@@ -1 +0,0 @@
|
||||
when creating new backup from schedule from cli, allow backup name to be automatically generated
|
||||
@@ -1 +0,0 @@
|
||||
log a warning instead of erroring if an additional item returned from a plugin can't be found in the Kubernetes API
|
||||
@@ -1 +0,0 @@
|
||||
Azure: support using `aad-pod-identity` auth when using restic
|
||||
@@ -1 +0,0 @@
|
||||
When a timeout string can't be parsed, log the error as a warning instead of silently consuming the error.
|
||||
@@ -1 +0,0 @@
|
||||
implement option to back up all volumes by default with restic
|
||||
@@ -1 +0,0 @@
|
||||
add a CI check for a changelog file
|
||||
@@ -1 +0,0 @@
|
||||
Add linter checks to Makefile
|
||||
@@ -1,6 +0,0 @@
|
||||
improve builder image handling so that we don't rebuild each `make shell`
|
||||
first check if there are pending changed on the build-image dockerfile if so build it.
|
||||
then check if there is an image in the registry if so pull it.
|
||||
then build an image cause we don't have a cached image. (this handles the backward compat case.)
|
||||
fix make clean to clear go mod cache before removing dirs (for containerized builds)
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
Documentation: Update Jekyll to 4.1.0
|
||||
|
||||
Switch from redcarpet to kramdown for Markdown renderer
|
||||
@@ -1 +0,0 @@
|
||||
Update basic-install.md documentation: Add windows cli installation option via chocolatey
|
||||
@@ -1 +0,0 @@
|
||||
Add a new supported provider for an object storage plugin for Storj
|
||||
@@ -1 +0,0 @@
|
||||
move CSI plugin out of prototype into beta
|
||||
@@ -1 +0,0 @@
|
||||
Update basic-install and release-instructions documentation for Windows Chocolatey package
|
||||
@@ -1 +0,0 @@
|
||||
bump Kubernetes module dependencies to v0.18.4 to fix https://github.com/vmware-tanzu/velero/issues/2540 by adding code compatibility with kubernetes v1.18
|
||||
@@ -1 +0,0 @@
|
||||
Improve velero download doc
|
||||
@@ -1 +0,0 @@
|
||||
Add a BSL controller to handle validation + update BSL status phase (validation removed from the server and no longer blocks when there's any invalid BSL)
|
||||
@@ -1 +0,0 @@
|
||||
updated acceptable values on cron schedule from 0-7 to 0-6
|
||||
@@ -1 +0,0 @@
|
||||
capture version of the CRD prior before invoking the remap_crd_version backup item action
|
||||
@@ -1 +0,0 @@
|
||||
Adjust restic default time out to 4 hours and base pod resource requests to 500m CPU/512Mi memory.
|
||||
@@ -1 +0,0 @@
|
||||
Add backupValidationFailureTotal to metrics
|
||||
@@ -303,6 +303,15 @@ spec:
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
orderedResources:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: OrderedResources specifies the backup order of resources
|
||||
of specific Kind. The map key is the Kind name and value is a list
|
||||
of resource names separeted by commas. Each resource name has format
|
||||
"namespace/resourcename". For cluster resources, simply use "resourcename".
|
||||
nullable: true
|
||||
type: object
|
||||
snapshotVolumes:
|
||||
description: SnapshotVolumes specifies whether to take cloud snapshots
|
||||
of any PV's referenced in the set of objects included in the Backup.
|
||||
|
||||
@@ -26,6 +26,8 @@ spec:
|
||||
kind: BackupStorageLocation
|
||||
listKind: BackupStorageLocationList
|
||||
plural: backupstoragelocations
|
||||
shortNames:
|
||||
- bsl
|
||||
singular: backupstoragelocation
|
||||
preserveUnknownFields: false
|
||||
scope: Namespaced
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -318,6 +318,16 @@ spec:
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
orderedResources:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: OrderedResources specifies the backup order of resources
|
||||
of specific Kind. The map key is the Kind name and value is a
|
||||
list of resource names separeted by commas. Each resource name
|
||||
has format "namespace/resourcename". For cluster resources, simply
|
||||
use "resourcename".
|
||||
nullable: true
|
||||
type: object
|
||||
snapshotVolumes:
|
||||
description: SnapshotVolumes specifies whether to take cloud snapshots
|
||||
of any PV's referenced in the set of objects included in the Backup.
|
||||
|
||||
@@ -13,9 +13,13 @@ spec:
|
||||
kind: ServerStatusRequest
|
||||
listKind: ServerStatusRequestList
|
||||
plural: serverstatusrequests
|
||||
shortNames:
|
||||
- ssr
|
||||
singular: serverstatusrequest
|
||||
preserveUnknownFields: false
|
||||
scope: Namespaced
|
||||
subresources:
|
||||
status: {}
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
description: ServerStatusRequest is a request to access current status information
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -26,3 +26,23 @@ rules:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- velero.io
|
||||
resources:
|
||||
- serverstatusrequests
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- velero.io
|
||||
resources:
|
||||
- serverstatusrequests/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
|
||||
25
config/samples/velero_v1_serverstatusrequest.yaml
Normal file
25
config/samples/velero_v1_serverstatusrequest.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
apiVersion: velero.io/v1
|
||||
kind: ServerStatusRequest
|
||||
metadata:
|
||||
creationTimestamp: "2020-08-21T15:34:34Z"
|
||||
generateName: velero-cli-
|
||||
generation: 1
|
||||
name: velero-cli-6wkzd
|
||||
namespace: velero
|
||||
resourceVersion: "544749"
|
||||
selfLink: /apis/velero.io/v1/namespaces/velero/serverstatusrequests/velero-cli-6wkzd
|
||||
uid: 335ea64e-1904-40ec-8106-1f2b22e9540e
|
||||
spec: {}
|
||||
status:
|
||||
phase: Processed
|
||||
plugins:
|
||||
- kind: ObjectStore
|
||||
name: velero.io/aws
|
||||
- kind: VolumeSnapshotter
|
||||
name: velero.io/aws
|
||||
- kind: BackupItemAction
|
||||
name: velero.io/crd-remap-version
|
||||
- kind: BackupItemAction
|
||||
name: velero.io/pod
|
||||
processedTimestamp: "2020-08-21T15:34:34Z"
|
||||
@@ -276,7 +276,7 @@ The value for these flags will be stored as annotations.
|
||||
|
||||
#### Handling CA certs
|
||||
|
||||
In anticipation of a new configuration implementation to handle custom CA certs (as per design doc https://github.com/vmware-tanzu/velero/blob/master/design/custom-ca-support.md), a new flag `velero storage-location create/set --cacert-file mapStringString` is proposed. It sets the configuration to use for creating a secret containing a custom certificate for an S3 location of a plugin provider. Format is provider:path-to-file.
|
||||
In anticipation of a new configuration implementation to handle custom CA certs (as per design doc https://github.com/vmware-tanzu/velero/blob/main/design/custom-ca-support.md), a new flag `velero storage-location create/set --cacert-file mapStringString` is proposed. It sets the configuration to use for creating a secret containing a custom certificate for an S3 location of a plugin provider. Format is provider:path-to-file.
|
||||
|
||||
See discussion https://github.com/vmware-tanzu/velero/pull/2259#discussion_r384700723 for more clarification.
|
||||
|
||||
@@ -370,4 +370,4 @@ https://github.com/jpeach/contour/tree/1c575c772e9fd747fba72ae41ab99bdae7a01864/
|
||||
|
||||
## Security Considerations
|
||||
|
||||
N/A
|
||||
N/A
|
||||
|
||||
@@ -306,16 +306,16 @@ Without these objects, the provider-level snapshots cannot be located in order t
|
||||
[1]: https://kubernetes.io/blog/2018/10/09/introducing-volume-snapshot-alpha-for-kubernetes/
|
||||
[2]: https://github.com/kubernetes-csi/external-snapshotter/blob/master/pkg/apis/volumesnapshot/v1alpha1/types.go#L41
|
||||
[3]: https://github.com/kubernetes-csi/external-snapshotter/blob/master/pkg/apis/volumesnapshot/v1alpha1/types.go#L161
|
||||
[4]: https://github.com/heptio/velero/blob/master/pkg/volume/snapshot.go#L21
|
||||
[5]: https://github.com/heptio/velero/blob/master/pkg/apis/velero/v1/pod_volume_backup.go#L88
|
||||
[4]: https://github.com/heptio/velero/blob/main/pkg/volume/snapshot.go#L21
|
||||
[5]: https://github.com/heptio/velero/blob/main/pkg/apis/velero/v1/pod_volume_backup.go#L88
|
||||
[6]: https://github.com/heptio/velero-csi-plugin/
|
||||
[7]: https://github.com/heptio/velero/blob/master/pkg/plugin/velero/volume_snapshotter.go#L26
|
||||
[8]: https://github.com/heptio/velero/blob/master/pkg/controller/backup_controller.go#L560
|
||||
[9]: https://github.com/heptio/velero/blob/master/pkg/persistence/object_store.go#L46
|
||||
[10]: https://github.com/heptio/velero/blob/master/pkg/apis/velero/v1/labels_annotations.go#L21
|
||||
[11]: https://github.com/heptio/velero/blob/master/pkg/cmd/server/server.go#L471
|
||||
[12]: https://github.com/heptio/velero/blob/master/pkg/cmd/util/output/backup_describer.go
|
||||
[13]: https://github.com/heptio/velero/blob/master/pkg/cmd/util/output/backup_describer.go#L214
|
||||
[7]: https://github.com/heptio/velero/blob/main/pkg/plugin/velero/volume_snapshotter.go#L26
|
||||
[8]: https://github.com/heptio/velero/blob/main/pkg/controller/backup_controller.go#L560
|
||||
[9]: https://github.com/heptio/velero/blob/main/pkg/persistence/object_store.go#L46
|
||||
[10]: https://github.com/heptio/velero/blob/main/pkg/apis/velero/v1/labels_annotations.go#L21
|
||||
[11]: https://github.com/heptio/velero/blob/main/pkg/cmd/server/server.go#L471
|
||||
[12]: https://github.com/heptio/velero/blob/main/pkg/cmd/util/output/backup_describer.go
|
||||
[13]: https://github.com/heptio/velero/blob/main/pkg/cmd/util/output/backup_describer.go#L214
|
||||
[14]: https://github.com/kubernetes/kubernetes/blob/8ea9edbb0290e9de1e6d274e816a4002892cca6f/pkg/controller/volume/persistentvolume/util/util.go#L69
|
||||
[15]: https://github.com/kubernetes/kubernetes/pull/30285
|
||||
[16]: https://github.com/kubernetes/kubernetes/blob/master/pkg/apis/core/types.go#L237
|
||||
|
||||
@@ -73,8 +73,8 @@ This same approach can be taken for CA bundles. The bundle can be stored in a
|
||||
secret which is referenced on the BSL and written to a temp file prior to
|
||||
invoking Restic.
|
||||
|
||||
[1](https://github.com/vmware-tanzu/velero/blob/master/pkg/restic/repository_manager.go#L238-L245)
|
||||
[2](https://github.com/vmware-tanzu/velero/blob/master/pkg/restic/common.go#L168-L203)
|
||||
[1](https://github.com/vmware-tanzu/velero/blob/main/pkg/restic/repository_manager.go#L238-L245)
|
||||
[2](https://github.com/vmware-tanzu/velero/blob/main/pkg/restic/common.go#L168-L203)
|
||||
|
||||
## Detailed Design
|
||||
|
||||
@@ -126,7 +126,7 @@ would look like:
|
||||
$ velero client config set cacert PATH
|
||||
```
|
||||
|
||||
[1]: https://github.com/vmware-tanzu/velero-plugin-for-aws/blob/master/velero-plugin-for-aws/object_store.go#L135
|
||||
[2]: https://github.com/vmware-tanzu/velero/blob/master/pkg/restic/command.go#L47
|
||||
[3]: https://github.com/restic/restic/blob/master/internal/backend/http_transport.go#L81
|
||||
[4]: https://github.com/vmware-tanzu/velero-plugin-for-aws/blob/master/velero-plugin-for-aws/object_store.go#L154
|
||||
[1]: https://github.com/vmware-tanzu/velero-plugin-for-aws/blob/main/velero-plugin-for-aws/object_store.go#L135
|
||||
[2]: https://github.com/vmware-tanzu/velero/blob/main/pkg/restic/command.go#L47
|
||||
[3]: https://github.com/restic/restic/blob/main/internal/backend/http_transport.go#L81
|
||||
[4]: https://github.com/vmware-tanzu/velero-plugin-for-aws/blob/main/velero-plugin-for-aws/object_store.go#L154
|
||||
|
||||
199
design/delete-item-action.md
Normal file
199
design/delete-item-action.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# Delete Item Action Plugins
|
||||
|
||||
## Abstract
|
||||
|
||||
Velero should provide a way to delete items created during a backup, with a model and interface similar to that of BackupItemAction and RestoreItemAction plugins.
|
||||
These plugins would be invoked when a backup is deleted, and would receive items from within the backup tarball.
|
||||
|
||||
## Background
|
||||
|
||||
As part of Container Storage Interface (CSI) snapshot support, Velero added a new pattern for backing up and restoring snapshots via BackupItemAction and RestoreItemAction plugins.
|
||||
When others have tried to use this pattern, however, they encountered issues with deleting the resources made in their own ItemAction plugins, as Velero does not expose any sort of extension at backup deletion time.
|
||||
These plugins largely seek to delete resources that exist outside of Kubernetes.
|
||||
This design seeks to provide the missing extension point.
|
||||
|
||||
## Goals
|
||||
|
||||
- Provide a DeleteItemAction API for plugins to implement
|
||||
- Update Velero backup deletion logic to invoke registered DeleteItemAction plugins.
|
||||
|
||||
## Non Goals
|
||||
|
||||
- Specific implementations of hte DeleteItemAction API beyond test cases.
|
||||
- Rollback of DeleteItemAction execution.
|
||||
|
||||
## High-Level Design
|
||||
|
||||
The DeleteItemAction plugin API will closely resemble the RestoreItemAction plugin design, in that plugins will receive the Velero `Backup` Go struct that is being deleted and a matching Kubernetes resource extracted from the backup tarball.
|
||||
|
||||
The Velero backup deletion process will be modified so that if there are any DeleteItemAction plugins registered, the backup tarball will be downloaded and extracted, similar to how restore logic works now.
|
||||
Then, each item in the backup tarball will be iterated over to see if a DeleteItemAction plugin matches for it.
|
||||
If a DeleteItemAction plugin matches, the `Backup` and relevant item will be passed to the DeleteItemAction.
|
||||
|
||||
The DeleteItemAction plugins will be run _first_ in the backup deletion process, before deleting snapshots from storage or `Restore`s from the Kubernetes API server.
|
||||
|
||||
DeleteItemAction plugins *cannot* rollback their actions.
|
||||
This is because there is currently no way to recover other deleted components of a backup, such as volume/restic snapshots or other DeleteItemAction resources.
|
||||
|
||||
DeleteItemAction plugins will be run in alphanumeric order based on their registered names.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### New types
|
||||
|
||||
The `DeleteItemAction` interface is as follows:
|
||||
|
||||
```go
|
||||
// DeleteItemAction is an actor that performs an action based on an item in a backup that is being deleted.
|
||||
type DeleteItemAction interface {
|
||||
// AppliesTo returns information about which resources this action should be invoked for.
|
||||
// A DeleteItemAction's Execute function will only be invoked on items that match the returned
|
||||
// selector. A zero-valued ResourceSelector matches all resources.
|
||||
AppliesTo() (ResourceSelector, error)
|
||||
|
||||
// Execute allows the ItemAction to perform arbitrary logic with the item being deleted.
|
||||
Execute(DeleteItemActionInput) error
|
||||
}
|
||||
```
|
||||
|
||||
The `DeleteItemActionInput` type is defined as follows:
|
||||
|
||||
```go
|
||||
type DeleteItemActionInput struct {
|
||||
// Item is the item taken from the pristine backed up version of resource.
|
||||
Item runtime.Unstructured
|
||||
// Backup is the representation of the backup resource processed by Velero.
|
||||
Backup *api.Backup
|
||||
}
|
||||
```
|
||||
|
||||
Both `DeleteItemAction` and `DeleteItemActionInput` will be defined in `pkg/plugin/velero/delete_item_action.go`.
|
||||
|
||||
### Generate protobuf definitions and client/servers
|
||||
|
||||
In `pkg/plugin/proto`, add `DeleteItemAction.proto`.
|
||||
|
||||
Protobuf definitions will be necessary for:
|
||||
|
||||
```protobuf
|
||||
message DeleteItemActionExecuteRequest {
|
||||
...
|
||||
}
|
||||
|
||||
message DeleteItemActionExecuteResponse {
|
||||
...
|
||||
}
|
||||
|
||||
message DeleteItemActionAppliesToRequest {
|
||||
...
|
||||
}
|
||||
|
||||
message DeleteItemActionAppliesToResponse {
|
||||
...
|
||||
}
|
||||
|
||||
service DeleteItemAction {
|
||||
rpc AppliesTo(DeleteItemActionAppliesToRequest) returns (DeleteItemActionAppliesToResponse)
|
||||
rpc Execute(DeleteItemActionExecuteRequest) returns (DeleteItemActionExecuteResponse)
|
||||
}
|
||||
```
|
||||
|
||||
Once these are written, then a client and server implementation can be written in `pkg/plugin/framework/delete_item_action_client.go` and `pkg/plugin/framework/delete_item_action_server.go`, respectively.
|
||||
These should be largely the same as the client and server implementations for `RestoreItemAction` and `BackupItemAction` plugins.
|
||||
|
||||
### Restartable delete plugins
|
||||
|
||||
Similar to `RestoreItemAction` and `BackupItemAction` plugins, restartable processes will need to be implemented.
|
||||
|
||||
In `pkg/plugin/clientmgmt`, add `restartable_delete_item_action.go`, creating the following unexported type:
|
||||
|
||||
```go
|
||||
type restartableDeleteItemAction struct {
|
||||
key kindAndName
|
||||
sharedPluginProcess RestartableProcess
|
||||
config map[string]string
|
||||
}
|
||||
|
||||
// newRestartableDeleteItemAction returns a new restartableDeleteItemAction.
|
||||
func newRestartableDeleteItemAction(name string, sharedPluginProcess RestartableProcess) *restartableDeleteItemAction {
|
||||
// ...
|
||||
}
|
||||
|
||||
// getDeleteItemAction returns the delete item action for this restartableDeleteItemAction. It does *not* restart the
|
||||
// plugin process.
|
||||
func (r *restartableDeleteItemAction) getDeleteItemAction() (velero.DeleteItemAction, error) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// getDelegate restarts the plugin process (if needed) and returns the delete item action for this restartableDeleteItemAction.
|
||||
func (r *restartableDeleteItemAction) getDelegate() (velero.DeleteItemAction, error) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// AppliesTo restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableDeleteItemAction) AppliesTo() (velero.ResourceSelector, error) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Execute restarts the plugin's process if needed, then delegates the call.
|
||||
func (r *restartableDeleteItemAction) Execute(input *velero.DeleteItemActionInput) (error) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
This file will be very similar in structure to
|
||||
|
||||
### Plugin manager changes
|
||||
|
||||
Add the following methods to `pkg/plugin/clientmgmt/manager.go`'s `Manager` interface:
|
||||
|
||||
```go
|
||||
type Manager interface {
|
||||
...
|
||||
// Get DeleteItemAction returns a DeleteItemAction plugin for name.
|
||||
GetDeleteItemAction(name string) (DeleteItemAction, error)
|
||||
|
||||
// GetDeteteItemActions returns the all DeleteItemAction plugins.
|
||||
GetDeleteItemActions() ([]DeleteItemAction, error)
|
||||
}
|
||||
```
|
||||
|
||||
The unexported `manager` type should implement both the `GetDeleteItemAction` and `GetDeleteItemActions`.
|
||||
|
||||
Both of these methods should have the same exception for `velero.io/`-prefixed plugins that all other types do.
|
||||
|
||||
`GetDeleteItemAction` and `GetDeleteItemActions` will invoke the `restartableDeleteItemAction` implementations.
|
||||
|
||||
|
||||
### Deletion controller modifications
|
||||
|
||||
`pkg/controller/backup_deletion_controller.go` will be updated to have plugin management invoked.
|
||||
|
||||
In `processRequest`, before deleting snapshots, get any registered `DeleteItemAction` plugins.
|
||||
If there are none, proceed as normal.
|
||||
If there are one or more, download the backup tarball from backup storage, untar it to temporary storage, and iterate through the items, matching them to the applicable plugins.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
Another proposal for higher level `DeleteItemActions` was initially included, which would require implementors to individually download the backup tarball themselves.
|
||||
While this may be useful long term, it is not a good fit for the current goals as each plugin would be re-implementing a lot of boilerplate.
|
||||
See the deletion-plugins.md file for this alternative proposal in more detail.
|
||||
|
||||
The `VolumeSnapshotter` interface is not generic enough to meet the requirements here, as it is specifically for taking snapshots of block devices.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
By their nature, `DeleteItemAction` plugins will be deleting data, which would normally be a security concern.
|
||||
However, these will only be invoked in two situations: either when a `BackupDeleteRequest` is sent via a user with the `velero` CLI or some other management system, or when a Velero `Backup` expires by going over its TTL.
|
||||
Because of this, the data deletion is not a concern.
|
||||
|
||||
## Compatibility
|
||||
|
||||
In terms of backwards compatibility, this design should stay compatible with most Velero installations that are upgrading.
|
||||
If not DeleteItemAction plugins are present, then the backup deletion process should proceed the same way it worked prior to their inclusion.
|
||||
|
||||
## Implementation
|
||||
|
||||
The implementation dependencies are, roughly, in the order as they are described in the [Detailed Design](#detailed-design) section.
|
||||
|
||||
## Open Issues
|
||||
82
design/deletion-plugins.md
Normal file
82
design/deletion-plugins.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Deletion Plugins
|
||||
|
||||
Status: Alternative Proposal
|
||||
|
||||
|
||||
## Abstract
|
||||
Velero should introduce a new type of plugin that runs when a backup is deleted.
|
||||
These plugins will delete any external resources associated with the backup so that they will not be left orphaned.
|
||||
|
||||
## Background
|
||||
With the CSI plugin, Velero developers introduced a pattern of using BackupItemAction and RestoreItemAction plugins tied to PersistentVolumeClaims to create other resources to complete a backup.
|
||||
In the CSI plugin case, Velero does clean up of these other resources, which are Kubernetes Custom Resources, within the core Velero server.
|
||||
However, for external plugins that wish to use this same pattern, this is not a practical solution.
|
||||
Velero's core cannot be extended for all possible Custom Resources, and not external resources that get created are Kubernetes Custom Resources.
|
||||
|
||||
Therefore, Velero needs some mechanism that allows plugin authors who have created resources within a BackupItemAction or RestoreItemAction plugin to ensure those resources are deleted, regardless of what system those resources reside in.
|
||||
|
||||
## Goals
|
||||
- Provide a new plugin type in Velero that is invoked when a backup is deleted.
|
||||
|
||||
## Non Goals
|
||||
- Implementations of specific deletion plugins.
|
||||
- Rollback of deletion plugin execution.
|
||||
|
||||
|
||||
## High-Level Design
|
||||
Velero will provide a new plugin type that is similar to its existing plugin architecture.
|
||||
These plugins will be referred to as `DeleteAction` plugins.
|
||||
`DeleteAction` plugins will receive the `Backup` CustomResource being deleted on execution.
|
||||
|
||||
`DeleteAction` plugins cannot prevent deletion of an item.
|
||||
This is because multiple `DeleteAction` plugins can be registered, and this proposal does not include rollback and undoing of a deletion action.
|
||||
Thus, if multiple `DeleteAction` plugins have already run but another would request the deletion of a backup stopped, the backup that's retained would be inconsistent.
|
||||
|
||||
`DeleteActions` will apply to `Backup`s based on a label on the `Backup` itself.
|
||||
In order to ensure that `Backup`s don't execute `DeleteAction` plugins that are not relevant to them, `DeleteAction` plugins can register an `AppliesTo` function which will define a label selector on Velero backups.
|
||||
|
||||
`DeleteActions` will be run in alphanumerical order by plugin name.
|
||||
This order is somewhat arbitrary, but will be used to give authors and users a somewhat predictable order of events.
|
||||
|
||||
## Detailed Design
|
||||
The `DeleteAction` plugins will implement the following Go interface, defined in `pkg/plugin/velero/deletion_action.go`:
|
||||
|
||||
```go
|
||||
type DeleteAction struct {
|
||||
|
||||
// AppliesTo will match the DeleteAction plugin against Velero Backups that it should operate against.
|
||||
AppliesTo()
|
||||
|
||||
// Execute runs the custom plugin logic and may connect to external services.
|
||||
Execute(backup *api.backup) error
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The following methods would be added to the `clientmgmt.Manager` interface in `pkg/pluginclientmgmt/manager.go`:
|
||||
|
||||
```
|
||||
type Manager interface {
|
||||
...
|
||||
|
||||
// GetDeleteActions returns the registered DeleteActions.
|
||||
//TODO: do we need to get these by name, or can we get them all?
|
||||
GetDeleteActions([]velero.DeleteAction, error)
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
## Alternatives Considered
|
||||
TODO
|
||||
|
||||
## Security Considerations
|
||||
TODO
|
||||
|
||||
## Compatibility
|
||||
Backwards compatibility should be straight-forward; if there are no installed `DeleteAction` plugins, then the backup deletion process will proceed as it does today.
|
||||
|
||||
## Implementation
|
||||
TODO
|
||||
|
||||
## Open Issues
|
||||
In order to add a custom label to the backup, the backup must be modifiable inside of the `BackupItemActon` and `RestoreItemAction` plugins, which it currently is not. A work around for now is for the user to apply a label to the backup at creation time, but that is not ideal.
|
||||
@@ -3,7 +3,7 @@
|
||||
Status: Accepted
|
||||
|
||||
Some features may take a while to get fully implemented, and we don't necessarily want to have long-lived feature branches
|
||||
A simple feature flag implementation allows code to be merged into master, but not used unless a flag is set.
|
||||
A simple feature flag implementation allows code to be merged into main, but not used unless a flag is set.
|
||||
|
||||
## Goals
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ In order to know the CR created for the particular backup of a volume, Velero ad
|
||||
- `velero.io/pv-name` with value as volume that is undergoing backup
|
||||
|
||||
Backup name being unique won't cause issues like duplicates in identifying the CR.
|
||||
Labels will be set with the value returned from `GetValidName` function. (https://github.com/vmware-tanzu/velero/blob/master/pkg/label/label.go#L35).
|
||||
Labels will be set with the value returned from `GetValidName` function. (https://github.com/vmware-tanzu/velero/blob/main/pkg/label/label.go#L35).
|
||||
|
||||
If Plugin supports showing progress of the operation it is performing, it does following:
|
||||
- finds the VolumePluginBackup CR related to this backup operation by using `tags` passed in CreateSnapshot call
|
||||
@@ -281,7 +281,7 @@ In order to know the CR created for the particular restore of a volume, Velero a
|
||||
- `velero.io/snapshot-id` with value as snapshot id that need to be restored
|
||||
- `velero.io/provider` with value as `Provider` in `VolumeSnapshotLocation`
|
||||
|
||||
Labels will be set with the value returned from `GetValidName` function. (https://github.com/vmware-tanzu/velero/blob/master/pkg/label/label.go#L35).
|
||||
Labels will be set with the value returned from `GetValidName` function. (https://github.com/vmware-tanzu/velero/blob/main/pkg/label/label.go#L35).
|
||||
|
||||
Plugin will be able to identify CR by using snapshotID that it received as parameter of CreateVolumeFromSnapshot API, and plugin's Provider name.
|
||||
It updates the progress of restore operation regularly if plugin supports feature of showing progress.
|
||||
@@ -376,7 +376,7 @@ In order to know the CR created for the particular backup of a volume, volume sn
|
||||
|
||||
Backup name being unique won't cause issues like duplicates in identifying the CR.
|
||||
|
||||
Plugin need to sanitize the value that can be set for above labels. Label need to be set with the value returned from `GetValidName` function. (https://github.com/vmware-tanzu/velero/blob/master/pkg/label/label.go#L35).
|
||||
Plugin need to sanitize the value that can be set for above labels. Label need to be set with the value returned from `GetValidName` function. (https://github.com/vmware-tanzu/velero/blob/main/pkg/label/label.go#L35).
|
||||
|
||||
Though no restrictions are required on the name of CR, as a general practice, volume snapshotter can name this CR with the value same as return value of CreateSnapshot.
|
||||
|
||||
|
||||
@@ -32,14 +32,14 @@ It will also update the `spec.volumeName` of the related persistent volume claim
|
||||
|
||||
## Detailed Design
|
||||
|
||||
In `pkg/restore/restore.go`, around [line 872](https://github.com/heptio/velero/blob/master/pkg/restore/restore.go#L872), Velero has special-case code for persistent volumes.
|
||||
In `pkg/restore/restore.go`, around [line 872](https://github.com/vmware-tanzu/velero/blob/main/pkg/restore/restore.go#L872), Velero has special-case code for persistent volumes.
|
||||
This code will be updated to check for the two preconditions described in the previous section.
|
||||
If the preconditions are met, the object will be given a new name.
|
||||
The persistent volume will also be annotated with the original name, e.g. `velero.io/original-pv-name=NAME`.
|
||||
Importantly, the name change will occur **before** [line 890](https://github.com/heptio/velero/blob/master/pkg/restore/restore.go#L890), where Velero checks to see if it should restore the persistent volume.
|
||||
Importantly, the name change will occur **before** [line 890](https://github.com/vmware-tanzu/velero/blob/main/pkg/restore/restore.go#L890), where Velero checks to see if it should restore the persistent volume.
|
||||
Additionally, the old and new persistent volume names will be recorded in a new field that will be added to the `context` struct, `renamedPVs map[string]string`.
|
||||
|
||||
In the special-case code for persistent volume claims starting on [line 987](https://github.com/heptio/velero/blob/master/pkg/restore/restore.go#L987), Velero will check to see if the claimed persistent volume has been renamed by looking in `ctx.renamedPVs`.
|
||||
In the special-case code for persistent volume claims starting on [line 987](https://github.com/heptio/velero/blob/main/pkg/restore/restore.go#L987), Velero will check to see if the claimed persistent volume has been renamed by looking in `ctx.renamedPVs`.
|
||||
If so, Velero will update the persistent volume claim's `spec.volumeName` to the new name.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
143
design/restore-hooks.md
Normal file
143
design/restore-hooks.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Restore Hooks
|
||||
|
||||
This document proposes a solution that allows a user to specify Restore Hooks, much like Backup Hooks, that can be executed during the restore process.
|
||||
|
||||
## Goals
|
||||
|
||||
- Enable custom commands to be run during a restore in order to mirror the commands that are available to the backup process.
|
||||
- Provide observability into the result of commands run in restored pods.
|
||||
|
||||
## Non Goals
|
||||
|
||||
- Handling any application specific scenarios (postgres, mongo, etc)
|
||||
|
||||
## Background
|
||||
|
||||
Velero supports Backup Hooks to execute commands before and/or after a backup.
|
||||
This enables a user to, among other things, prepare data to be backed up without having to freeze an in-use volume.
|
||||
An example of this would be to attach an empty volume to a Postgres pod, use a backup hook to execute `pg_dump` from the data volume, and back up the volume containing the export.
|
||||
The problem is that there's no easy or automated way to include an automated restore process.
|
||||
After a restore with the example configuration above, the postgres pod will be empty, but there will be a need to manually exec in and run `pg_restore`.
|
||||
|
||||
## High-Level Design
|
||||
|
||||
The Restore spec will have a `spec.hooks` section matching the same section on the Backup spec except no `pre` hooks can be defined - only `post`.
|
||||
Annotations comparable to the annotations used during backup can also be set on pods.
|
||||
For each restored pod, the Velero server will check if there are any hooks applicable to the pod.
|
||||
If a restored pod has any applicable hooks, Velero will wait for the container where the hook is to be executed to reach status Running.
|
||||
The Restore log will include the results of each post-restore hook and the Restore object status will incorporate the results of hooks.
|
||||
The Restore log will include the results of each hook and the Restore object status will incorporate the results of hooks.
|
||||
|
||||
A new section at `spec.hooks.resources.initContainers` will allow for injecting initContainers into restored pods.
|
||||
Annotations can be set as an alternative to defining the initContainers in the Restore object.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
Post-restore hooks can be defined by annotation and/or by an array of resource hooks in the Restore spec.
|
||||
|
||||
The following annotations are supported:
|
||||
- post.hook.restore.velero.io/container
|
||||
- post.hook.restore.velero.io/command
|
||||
- post.hook.restore.velero.io/on-error
|
||||
- post.hook.restore.velero.io/exec-timeout
|
||||
- post.hook.restore.velero.io/wait-timeout
|
||||
|
||||
Init restore hooks can be defined by annotation and/or in the new `initContainers` section in the Restore spec.
|
||||
The initContainers schema is `pod.spec.initContainers`.
|
||||
|
||||
The following annotations are supported:
|
||||
- init.hook.restore.velero.io/timeout
|
||||
- init.hook.restore.velero.io/initContainers
|
||||
|
||||
This is an example of defining hooks in the Restore spec.
|
||||
|
||||
```yaml
|
||||
apiVersion: velero.io/v1
|
||||
kind: Restore
|
||||
spec:
|
||||
...
|
||||
hooks:
|
||||
resources:
|
||||
-
|
||||
name: my-hook
|
||||
includedNamespaces:
|
||||
- '*'
|
||||
excludedNamespaces:
|
||||
- some-namespace
|
||||
includedResources:
|
||||
- pods
|
||||
excludedResources: []
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app: velero
|
||||
component: server
|
||||
post:
|
||||
-
|
||||
exec:
|
||||
container: postgres
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- rm /docker-entrypoint-initdb.d/dump.sql
|
||||
onError: Fail
|
||||
timeout: 10s
|
||||
readyTimeout: 60s
|
||||
init:
|
||||
timeout: 120s
|
||||
initContainers:
|
||||
- name: restore
|
||||
image: postgres:12
|
||||
command: ["/bin/bash", "-c", "mv /backup/dump.sql /docker-entrypoint-initdb.d/"]
|
||||
volumeMounts:
|
||||
- name: backup
|
||||
mountPath: /backup
|
||||
```
|
||||
|
||||
As with Backups, if an annotation is defined on a pod then no hooks from the Restore spec will be applied.
|
||||
|
||||
### Implementation
|
||||
|
||||
The types and function in pkg/backup/item_hook_handler.go will be moved to a new package (pkg/hooks) and exported so they can be used for both backups and restores.
|
||||
|
||||
The post-restore hooks implementation will closely follow the design of restoring pod volumes with restic.
|
||||
The pkg/restore.context type will have new fields `hooksWaitGroup` and `hooksErrs` comparable to `resticWaitGroup` and `resticErr`.
|
||||
The pkg/restore.context.execute function will start a goroutine for each pod with applicable hooks and then continue with restoring other items.
|
||||
Each hooks goroutine will create a pkg/util/hooks.ItemHookHandler for each pod and send any error on the context.hooksErrs channel.
|
||||
The ItemHookHandler already includes stdout and stderr and other metadata in the Backup log so the same logs will automatically be added to the Restore log (passed as the first argument to the ItemHookhandler.HandleHooks method.)
|
||||
|
||||
The pkg/restore.context.execute function will wait for the hooksWaitGroup before returning.
|
||||
Any errors received on context.hooksErrs will be added to errs.Velero.
|
||||
|
||||
One difference compared to the restic restore design is that any error on the context.hooksErrs channel will cancel the context of all hooks, since errors are only reported on this channel if the hook specified `onError: Fail`.
|
||||
However, canceling the hooks goroutines will not cancel the restic goroutines.
|
||||
In practice the restic goroutines will complete before the hooks since the hooks do not run until a pod is ready, but it's possible a hook will be executed and fail while a different pod is still in the pod volume restore phase.
|
||||
|
||||
Failed hooks with `onError: Continue` will appear in the Restore log but will not affect the status of the parent Restore.
|
||||
Failed hooks with `onError: Fail` will cause the parent Restore to have status Partially Failed.
|
||||
|
||||
If initContainers are specified for a pod, Velero will inject the containers into the beginning of the pod's initContainers list.
|
||||
If a restic initContainer is also being injected, the restore initContainers will be injected directly after the restic initContainer.
|
||||
The restore will use a RestoreItemAction to inject the initContainers.
|
||||
Stdout and stderr of the restore initContainers will not be added to the Restore logs.
|
||||
InitContainers that fail will not affect the parent Restore's status.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
Wait for all restored Pods to report Ready, then execute the first hook in all applicable Pods simultaneously, then proceed to the next hook, etc.
|
||||
That could introduce deadlock, e.g. if an API pod cannot be ready until the DB pod is restored.
|
||||
|
||||
Put the restore hooks on the Backup spec as a third lifecycle event named `restore` along with `pre` and `post`.
|
||||
That would be confusing since `pre` and `post` would appear in the Backup log but `restore` would only be in the Restore log.
|
||||
|
||||
Execute restore hooks in parallel for each Pod.
|
||||
That would not match the behavior of Backups.
|
||||
|
||||
Wait for PodStatus ready before executing the post-restore hooks in any container.
|
||||
There are cases where the pod should not report itself ready until after the restore hook has run.
|
||||
|
||||
Include the logs from initContainers in the Restore log.
|
||||
Unlike exec hooks where stdout and stderr are permanently lost if not added to the Restore log, the logs of the injected initContainers are available through the K8s API with kubectl or another client.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
Stdout or stderr in the Restore log may contain sensitive information, but the same risk already exists for Backup hooks.
|
||||
11
go.mod
11
go.mod
@@ -21,22 +21,25 @@ require (
|
||||
github.com/kubernetes-csi/external-snapshotter/v2 v2.2.0-rc1
|
||||
github.com/onsi/ginkgo v1.13.0
|
||||
github.com/onsi/gomega v1.10.1
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/prometheus/client_golang v1.0.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.5.1
|
||||
github.com/robfig/cron v1.1.0
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/spf13/afero v1.2.2
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/spf13/cobra v0.0.6
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.4.0
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7
|
||||
google.golang.org/grpc v1.28.0
|
||||
google.golang.org/genproto v0.0.0-20200731012542-8145dea6a485 // indirect
|
||||
google.golang.org/grpc v1.31.0
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
k8s.io/api v0.18.4
|
||||
k8s.io/apiextensions-apiserver v0.18.4
|
||||
k8s.io/apimachinery v0.18.4
|
||||
k8s.io/cli-runtime v0.18.4
|
||||
k8s.io/client-go v0.18.4
|
||||
k8s.io/klog v1.0.0
|
||||
sigs.k8s.io/cluster-api v0.3.8
|
||||
sigs.k8s.io/controller-runtime v0.6.1
|
||||
)
|
||||
|
||||
|
||||
138
go.sum
138
go.sum
@@ -14,16 +14,12 @@ github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo
|
||||
github.com/Azure/azure-sdk-for-go v42.0.0+incompatible h1:yz6sFf5bHZ+gEOQVuK5JhPqTTAmv+OvSLSaqgzqaCwY=
|
||||
github.com/Azure/azure-sdk-for-go v42.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest v0.9.3 h1:OZEIaBbMdUE/Js+BQKlpO81XlISgipr6yDJ+PSwsgi4=
|
||||
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
|
||||
github.com/Azure/go-autorest/autorest v0.9.6 h1:5YWtOnckcudzIw8lPPBcWOnmIFWMtHci1ZWAZulMSx0=
|
||||
github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.1 h1:pZdL8o72rK+avFWl+p9nE8RWi1JInZrWJYlnpfXJwHk=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||
@@ -31,12 +27,10 @@ github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 h1:iM6UAvjR97ZIeR93qTcwp
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 h1:LXl088ZQlP0SBppGFsRZonW6hSvwgL5gRByMbvUbx8U=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
@@ -55,25 +49,28 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
||||
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
|
||||
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/hcsshim v0.0.0-20190417211021-672e52e9209d/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OpenPeeDeeP/depguard v1.0.0/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o=
|
||||
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/Rican7/retry v0.1.0/go.mod h1:FgOROf8P5bebcC1DS0PdOQiqGUridaZvikzUmkFW6gg=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053/go.mod h1:xW8sBma2LE3QxFSzCnH9qe6gAE2yO9GvQaWwX89HxbE=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
@@ -89,19 +86,25 @@ github.com/bazelbuild/buildtools v0.0.0-20190731111112-f720930ceb60/go.mod h1:5J
|
||||
github.com/bazelbuild/buildtools v0.0.0-20190917191645-69366ca98f89/go.mod h1:5JP0TXzWDHXv8qvxRC4InIazwdyDseBDbzESUMKk1yU=
|
||||
github.com/bazelbuild/rules_go v0.0.0-20190719190356-6dae44dc5cab/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU=
|
||||
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/caddyserver/caddy v1.0.3 h1:i9gRhBgvc5ifchwWtSe7pDpsdS9+Q0Rw9oYQmYUTw1w=
|
||||
github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/prettybench v0.0.0-20150116022406-03b8cfe5406c/go.mod h1:Xe6ZsFhtM8HrDku0pxJ3/Lr51rwykrzgFwpmTzleatY=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||
github.com/checkpoint-restore/go-criu v0.0.0-20181120144056-17b0214f6c48/go.mod h1:TrMrLQfeENAPYPRsJuq3jsqdlRh3lvi6trTZJG8+tho=
|
||||
github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
@@ -118,17 +121,25 @@ github.com/containerd/containerd v1.0.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX
|
||||
github.com/containerd/typeurl v0.0.0-20190228175220-2a93cfde8c20/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||
github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||
github.com/coredns/corefile-migration v1.0.6/go.mod h1:OFwBp/Wc9dJt5cAZzHWMNhK1r5L0p0jDwIBc6j8NC8E=
|
||||
github.com/coredns/corefile-migration v1.0.7 h1:T2eOj/NKN1Q1W1bD9MFeZiBYryS0JlWT6aROAvVWFSs=
|
||||
github.com/coredns/corefile-migration v1.0.7/go.mod h1:OFwBp/Wc9dJt5cAZzHWMNhK1r5L0p0jDwIBc6j8NC8E=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -137,9 +148,11 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
|
||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
@@ -150,7 +163,10 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ
|
||||
github.com/docker/spdystream v0.0.0-20170912183627-bc6354cbbc29 h1:llBx5m8Gk0lrAaiLud2wktkX/e8haX7Ru0oVfQqtZQ4=
|
||||
github.com/docker/spdystream v0.0.0-20170912183627-bc6354cbbc29/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/drone/envsubst v1.0.3-0.20200709223903-efdb65b94e5a h1:pf3CyiWgjOLL7cjFos89AEOPCWSOoQt7tgbEk/SvBAg=
|
||||
github.com/drone/envsubst v1.0.3-0.20200709223903-efdb65b94e5a/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
@@ -182,8 +198,10 @@ github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG
|
||||
github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
||||
github.com/go-critic/go-critic v0.3.5-0.20190526074819-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54=
|
||||
@@ -277,6 +295,7 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
|
||||
@@ -300,14 +319,19 @@ github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAO
|
||||
github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8=
|
||||
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/cadvisor v0.35.0/go.mod h1:1nql6U13uTHaLYB8rLS5x9IJc2qT6Xd/Tr1sTX6NE48=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||
@@ -326,17 +350,24 @@ github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTV
|
||||
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
|
||||
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
|
||||
github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
|
||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
|
||||
github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd h1:rNuUHR+CvK1IS89MMtcF0EpcVMZtjKfPRp4MEmt/aTs=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
||||
@@ -349,6 +380,7 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
@@ -366,10 +398,12 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5i
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
@@ -412,6 +446,7 @@ github.com/lucas-clemente/quic-go v0.10.2/go.mod h1:hvaRS9IHjFLMq76puFJeWNfmn+H7
|
||||
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58=
|
||||
github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
@@ -424,6 +459,7 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
@@ -443,6 +479,7 @@ github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
@@ -467,6 +504,7 @@ github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@@ -485,6 +523,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v1.0.0-rc10/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runtime-spec v1.0.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
@@ -492,28 +532,39 @@ github.com/opencontainers/selinux v1.3.1-0.20190929122143-5215b1806f52/go.mod h1
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
|
||||
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/pquerna/ffjson v0.0.0-20180717144149-af8b230fcd20/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA=
|
||||
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI=
|
||||
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
||||
github.com/quobyte/api v0.1.2/go.mod h1:jL7lIHrmqQ7yh05OJ+eEEdHr0u/kmT1Ff9iHd+4H6VI=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
@@ -525,6 +576,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||
github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
|
||||
github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||
@@ -534,6 +586,7 @@ github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
@@ -542,20 +595,25 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs=
|
||||
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
@@ -564,6 +622,9 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E=
|
||||
github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
|
||||
github.com/storageos/go-api v0.0.0-20180912212459-343b3eff91fc/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -573,12 +634,17 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/thecodeteam/goscaleio v0.1.0/go.mod h1:68sdkZAsK8bvEwBlbQnlLS+xU+hvLYM/iQ8KXej1AwM=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/timakin/bodyclose v0.0.0-20190721030226-87058b9bfcec/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ultraware/funlen v0.0.1/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
||||
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
||||
@@ -592,10 +658,14 @@ github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv
|
||||
github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||
github.com/vishvananda/netns v0.0.0-20171111001504-be1fbeda1936/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
|
||||
github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
@@ -669,6 +739,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -681,8 +752,9 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -722,6 +794,8 @@ golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -789,8 +863,9 @@ google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@@ -799,25 +874,34 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191220175831-5c49e3ecc1c1 h1:PlscBL5CvF+v1mNR82G+i4kACGq2JQvKDnNq7LSS65o=
|
||||
google.golang.org/genproto v0.0.0-20191220175831-5c49e3ecc1c1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200731012542-8145dea6a485 h1:wTk5DQB3+1darAz4Ldomo0r5bUOCKX7gilxQ4sb2kno=
|
||||
google.golang.org/genproto v0.0.0-20200731012542-8145dea6a485/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -831,6 +915,8 @@ gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
@@ -842,9 +928,11 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/gotestsum v0.3.5/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY=
|
||||
@@ -861,14 +949,17 @@ k8s.io/apiextensions-apiserver v0.18.4 h1:Y3HGERmS8t9u12YNUFoOISqefaoGRuTc43AYCL
|
||||
k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio=
|
||||
k8s.io/apimachinery v0.18.4 h1:ST2beySjhqwJoIFk6p7Hp5v5O0hYY6Gngq/gUYXTPIA=
|
||||
k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
|
||||
k8s.io/apiserver v0.18.4 h1:pn1jSQkfboPSirZopkVpEdLW4FcQLnYMaIY8LFxxj30=
|
||||
k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8=
|
||||
k8s.io/cli-runtime v0.18.4 h1:IUx7quIOb4gbQ4M+B1ksF/PTBovQuL5tXWzplX3t+FM=
|
||||
k8s.io/cli-runtime v0.18.4/go.mod h1:9/hS/Cuf7NVzWR5F/5tyS6xsnclxoPLVtwhnkJG1Y4g=
|
||||
k8s.io/client-go v0.18.4 h1:un55V1Q/B3JO3A76eS0kUSywgGK/WR3BQ8fHQjNa6Zc=
|
||||
k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g=
|
||||
k8s.io/cloud-provider v0.18.4/go.mod h1:JdI6cuSFPSPANEciv0v5qfwztkeyFCVc1S3krLYrw0E=
|
||||
k8s.io/cluster-bootstrap v0.18.4 h1:TlH1HzHdANqE4d4Gw/9eENtGkEmAQVq8e4VGYPRupfc=
|
||||
k8s.io/cluster-bootstrap v0.18.4/go.mod h1:hNG705ec9SMN2BGlJ81R2CnyJjNKfROtAxvI9JXZdiM=
|
||||
k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
|
||||
k8s.io/component-base v0.18.4 h1:Kr53Fp1iCGNsl9Uv4VcRvLy7YyIqi9oaJOQ7SXtKI98=
|
||||
k8s.io/component-base v0.18.4/go.mod h1:7jr/Ef5PGmKwQhyAz/pjByxJbC58mhKAhiaDu0vXfPk=
|
||||
k8s.io/cri-api v0.18.4/go.mod h1:OJtpjDvfsKoLGhvcc0qfygved0S0dGX56IJzPbqTG1s=
|
||||
k8s.io/csi-translation-lib v0.18.4/go.mod h1:FTci2m8/3oN8E+8OyblBXei8w4mwbiH4boNPeob4piE=
|
||||
@@ -897,8 +988,9 @@ k8s.io/repo-infra v0.0.1-alpha.1/go.mod h1:wO1t9WaB99V80ljbeENTnayuEEwNZt7gECYh/
|
||||
k8s.io/sample-apiserver v0.18.4/go.mod h1:j5XH5FUmMd/ztoz+9ch0+hL+lsvWdgxnTV7l3P3Ijoo=
|
||||
k8s.io/system-validators v1.0.4/go.mod h1:HgSgTg4NAGNoYYjKsUyk52gdNi2PVDswQ9Iyn66R7NI=
|
||||
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 h1:v8ud2Up6QK1lNOKFgiIVrZdMg7MpmSnvtrOieolJKoE=
|
||||
k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20200619165400-6e3d28b6ed19 h1:7Nu2dTj82c6IaWvL7hImJzcXoTPz1MsSCH7r+0m6rfo=
|
||||
k8s.io/utils v0.0.0-20200619165400-6e3d28b6ed19/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
|
||||
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||
@@ -912,8 +1004,12 @@ rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
|
||||
sigs.k8s.io/cluster-api v0.3.8 h1:YYaAzXP+bHzqIQTJoJXMIT4QN4MjIep9NnxwBDPjiD8=
|
||||
sigs.k8s.io/cluster-api v0.3.8/go.mod h1:j2beYKyad77JkeGaiv4l2obt81C3eqxfZGFDqX0YWjs=
|
||||
sigs.k8s.io/controller-runtime v0.5.9/go.mod h1:UI/unU7Q+mo/rWBrND0NAaVNj/Xjh/+aqSv/M3njpmo=
|
||||
sigs.k8s.io/controller-runtime v0.6.1 h1:LcK2+nk0kmaOnKGN+vBcWHqY5WDJNJNB/c5pW+sU8fc=
|
||||
sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A=
|
||||
sigs.k8s.io/kind v0.7.1-0.20200303021537-981bd80d3802/go.mod h1:HIZ3PWUezpklcjkqpFbnYOqaqsAE1JeCTEwkgvPLXjk=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
|
||||
FROM golang:1.14
|
||||
|
||||
ARG GOPROXY
|
||||
|
||||
ENV GO111MODULE=on
|
||||
# Use a proxy for go modules to reduce the likelihood of various hosts being down and breaking the build
|
||||
ENV GOPROXY=https://proxy.golang.org
|
||||
ENV GOPROXY=${GOPROXY}
|
||||
|
||||
# get code-generation tools (for now keep in GOPATH since they're not fully modules-compatible yet)
|
||||
RUN mkdir -p /go/src/k8s.io
|
||||
@@ -54,4 +56,9 @@ RUN wget --quiet https://github.com/goreleaser/goreleaser/releases/download/v0.1
|
||||
chmod +x /usr/bin/goreleaser
|
||||
|
||||
# get golangci-lint
|
||||
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.27.0
|
||||
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.27.0
|
||||
|
||||
# install kubectl
|
||||
RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
|
||||
RUN chmod +x ./kubectl
|
||||
RUN mv ./kubectl /usr/local/bin
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
# Copyright 2016 The Kubernetes Authors.
|
||||
#
|
||||
# Modifications Copyright 2020 the Velero contributors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
@@ -18,40 +20,39 @@ set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
if [ -z "${PKG}" ]; then
|
||||
if [[ -z "${PKG}" ]]; then
|
||||
echo "PKG must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${BIN}" ]; then
|
||||
if [[ -z "${BIN}" ]]; then
|
||||
echo "BIN must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${GOOS}" ]; then
|
||||
if [[ -z "${GOOS}" ]]; then
|
||||
echo "GOOS must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${GOARCH}" ]; then
|
||||
if [[ -z "${GOARCH}" ]]; then
|
||||
echo "GOARCH must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${VERSION}" ]; then
|
||||
if [[ -z "${VERSION}" ]]; then
|
||||
echo "VERSION must be set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${GIT_SHA}" ]; then
|
||||
if [[ -z "${GIT_SHA}" ]]; then
|
||||
echo "GIT_SHA must be set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export CGO_ENABLED=0
|
||||
|
||||
if [[ -z "${GIT_DIRTY}" ]]; then
|
||||
GIT_TREE_STATE=clean
|
||||
else
|
||||
GIT_TREE_STATE=dirty
|
||||
if [[ -z "${GIT_TREE_STATE}" ]]; then
|
||||
echo "GIT_TREE_STATE must be set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export CGO_ENABLED=0
|
||||
|
||||
LDFLAGS="-X ${PKG}/pkg/buildinfo.Version=${VERSION}"
|
||||
LDFLAGS="${LDFLAGS} -X ${PKG}/pkg/buildinfo.GitSHA=${GIT_SHA}"
|
||||
LDFLAGS="${LDFLAGS} -X ${PKG}/pkg/buildinfo.GitTreeState=${GIT_TREE_STATE}"
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# docker-push is invoked by the CI/CD system to deploy docker images to Docker Hub.
|
||||
# It will build images for all commits to master and all git tags.
|
||||
# The highest, non-prerelease semantic version will also be given the `latest` tag.
|
||||
|
||||
set +x
|
||||
|
||||
@@ -37,7 +34,7 @@ if ls ${change_log_file} 1> /dev/null 2>&1; then
|
||||
echo "changelog for PR ${pr_number} exists"
|
||||
exit 0
|
||||
else
|
||||
echo "PR ${pr_number} is missing a changelog. Please refer https://velero.io/docs/master/code-standards/#adding-a-changelog and add a changelog."
|
||||
echo "PR ${pr_number} is missing a changelog. Please refer https://velero.io/docs/main/code-standards/#adding-a-changelog and add a changelog."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2019 the Velero contributors.
|
||||
# Copyright 2020 the Velero contributors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -15,7 +15,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
# docker-push is invoked by the CI/CD system to deploy docker images to Docker Hub.
|
||||
# It will build images for all commits to master and all git tags.
|
||||
# It will build images for all commits to main and all git tags.
|
||||
# The highest, non-prerelease semantic version will also be given the `latest` tag.
|
||||
|
||||
set +x
|
||||
@@ -56,34 +56,43 @@ elif [[ "$triggeredBy" == "tags" ]]; then
|
||||
TAG=$(echo $GITHUB_REF | cut -d / -f 3)
|
||||
fi
|
||||
|
||||
if [[ "$BRANCH" == "master" ]]; then
|
||||
if [[ "$BRANCH" == "main" ]]; then
|
||||
VERSION="$BRANCH"
|
||||
elif [[ ! -z "$TAG" ]]; then
|
||||
# Explicitly checkout tags when building from a git tag.
|
||||
# This is not needed when building from master
|
||||
# This is not needed when building from main
|
||||
git fetch --tags
|
||||
# Calculate the latest release if there's a tag.
|
||||
highest_release
|
||||
VERSION="$TAG"
|
||||
else
|
||||
echo "We're not on master and we're not building a tag, exit early."
|
||||
echo "We're not on main and we're not building a tag, exit early."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Assume we're not tagging `latest` by default, and never on master.
|
||||
# Assume we're not tagging `latest` by default, and never on main.
|
||||
TAG_LATEST=false
|
||||
if [[ "$BRANCH" == "master" ]]; then
|
||||
echo "Building master, not tagging latest."
|
||||
if [[ "$BRANCH" == "main" ]]; then
|
||||
echo "Building main, not tagging latest."
|
||||
elif [[ "$TAG" == "$HIGHEST" ]]; then
|
||||
TAG_LATEST=true
|
||||
fi
|
||||
|
||||
if [[ -z "$BUILDX_PLATFORMS" ]]; then
|
||||
BUILDX_PLATFORMS="linux/amd64,linux/arm64,linux/arm/v7,linux/ppc64le"
|
||||
fi
|
||||
|
||||
# Debugging info
|
||||
echo "Highest tag found: $HIGHEST"
|
||||
echo "BRANCH: $BRANCH"
|
||||
echo "TAG: $TAG"
|
||||
echo "TAG_LATEST: $TAG_LATEST"
|
||||
echo "BUILDX_PLATFORMS: $BUILDX_PLATFORMS"
|
||||
|
||||
echo "Building and pushing container images."
|
||||
|
||||
VERSION="$VERSION" TAG_LATEST="$TAG_LATEST" make all-containers all-push all-manifests
|
||||
VERSION="$VERSION" \
|
||||
TAG_LATEST="$TAG_LATEST" \
|
||||
BUILDX_PLATFORMS="$BUILDX_PLATFORMS" \
|
||||
BUILDX_OUTPUT_TYPE="registry" \
|
||||
make all-containers
|
||||
|
||||
54
hack/download-restic.sh
Executable file
54
hack/download-restic.sh
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2020 the Velero contributors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
if [[ -z "${BIN}" ]]; then
|
||||
echo "BIN must be set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${BIN}" != "velero" ]]; then
|
||||
echo "${BIN} does not need the restic binary"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -z "${GOOS}" ]]; then
|
||||
echo "GOOS must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${GOARCH}" ]]; then
|
||||
echo "GOARCH must be set"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${RESTIC_VERSION}" ]]; then
|
||||
echo "RESTIC_VERSION must be set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# TODO: when the new restic version is released, make ppc64le to be also downloaded from their github releases.
|
||||
# This has been merged and will be applied to next release: https://github.com/restic/restic/pull/2342
|
||||
if [[ "${GOARCH}" = "ppc64le" ]]; then
|
||||
wget --timeout=1 --tries=5 --quiet https://oplab9.parqtec.unicamp.br/pub/ppc64el/restic/restic-${RESTIC_VERSION} -O /output/usr/bin/restic
|
||||
else
|
||||
wget --quiet https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/restic_${RESTIC_VERSION}_${GOOS}_${GOARCH}.bz2
|
||||
bunzip2 restic_${RESTIC_VERSION}_${GOOS}_${GOARCH}.bz2
|
||||
mv restic_${RESTIC_VERSION}_${GOOS}_${GOARCH} /output/usr/bin/restic
|
||||
fi
|
||||
|
||||
chmod +x /output/usr/bin/restic
|
||||
108
hack/gen-docs.sh
108
hack/gen-docs.sh
@@ -1,108 +0,0 @@
|
||||
#!/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/$VELERO_VERSION|g"
|
||||
find site/docs/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i '' "s|https://github.com/vmware-tanzu/velero/blob/master|https://github.com/vmware-tanzu/velero/blob/$VELERO_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/$VELERO_VERSION|g"
|
||||
find site/docs/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i'' "s|https://github.com/vmware-tanzu/velero/blob/master|https://github.com/vmware-tanzu/velero/blob/$VELERO_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,34 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
if [ -z "${RESTIC_VERSION}" ]; then
|
||||
echo "RESTIC_VERSION must be set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "_output/bin/linux/ppc64le/" ]; then
|
||||
mkdir -p _output/bin/linux/ppc64le/
|
||||
fi
|
||||
|
||||
wget --quiet https://oplab9.parqtec.unicamp.br/pub/ppc64el/restic/restic-${RESTIC_VERSION}
|
||||
mv restic-${RESTIC_VERSION} _output/bin/linux/ppc64le/restic
|
||||
chmod +x _output/bin/linux/ppc64le/restic
|
||||
|
||||
@@ -22,13 +22,13 @@ import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// This regex should match both our GA format (example: v1.4.3) and pre-release format (v1.2.4-beta.2)
|
||||
// This regex should match both our GA format (example: v1.4.3) and pre-release formats (v1.2.4-beta.2, v1.5.0-rc.1)
|
||||
// The following sub-capture groups are defined:
|
||||
// major
|
||||
// minor
|
||||
// patch
|
||||
// prerelease (this will be alpha/beta followed by a ".", followed by 1 or more digits (alpha.5)
|
||||
var release_regex *regexp.Regexp = regexp.MustCompile("^v(?P<major>[[:digit:]]+)\\.(?P<minor>[[:digit:]]+)\\.(?P<patch>[[:digit:]]+)(-{1}(?P<prerelease>(alpha|beta)\\.[[:digit:]]+))*")
|
||||
// prerelease (this will be alpha/beta/rc followed by a ".", followed by 1 or more digits (alpha.5)
|
||||
var release_regex *regexp.Regexp = regexp.MustCompile("^v(?P<major>[[:digit:]]+)\\.(?P<minor>[[:digit:]]+)\\.(?P<patch>[[:digit:]]+)(-{1}(?P<prerelease>(alpha|beta|rc)\\.[[:digit:]]+))*")
|
||||
|
||||
// This small program exists because checking the VELERO_VERSION rules in bash is difficult, and difficult to test for correctness.
|
||||
// Calling it with --verify will verify whether or not the VELERO_VERSION environment variable is a valid version string, without parsing for its components.
|
||||
|
||||
142
hack/release-tools/gen-docs.sh
Executable file
142
hack/release-tools/gen-docs.sh
Executable file
@@ -0,0 +1,142 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2020 the Velero contributors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# gen-docs.sh is used for the "make gen-docs" target. It generates a new
|
||||
# versioned docs directory under site/content/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
|
||||
# 'main' 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 main ToC.
|
||||
# 6. Update site/config.yaml 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.
|
||||
#
|
||||
# NEW_DOCS_VERSION defines the version that the docs will be tagged with
|
||||
# (i.e. what’s in the URL, what shows up in the version dropdown on the site).
|
||||
# This should be formatted as either v1.4 (for any GA release, including minor), or v1.5.0-beta.1/v1.5.0-rc.1 (for an alpha/beta/RC).
|
||||
|
||||
# To run gen-docs: "VELERO_VERSION=v1.4.0 NEW_DOCS_VERSION=v1.4 PREVIOUS_DOCS_VERSION= make gen-docs"
|
||||
# Note: if PREVIOUS_DOCS_VERSION is not set, the script will copy from the
|
||||
# latest version.
|
||||
#
|
||||
# **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-HUGO.md
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
DOCS_DIRECTORY=site/content/docs
|
||||
DATA_DOCS_DIRECTORY=site/data/docs
|
||||
CONFIG_FILE=site/config.yaml
|
||||
MAIN_BRANCH=main
|
||||
|
||||
# don't run if there's already a directory for the target docs version
|
||||
if [[ -d $DOCS_DIRECTORY/$NEW_DOCS_VERSION ]]; then
|
||||
echo "ERROR: $DOCS_DIRECTORY/$NEW_DOCS_VERSION already exists"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# get the alphabetically last item in $DOCS_DIRECTORY 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 $DOCS_DIRECTORY/ | tail -n 1)
|
||||
fi
|
||||
|
||||
# make a copy of the previous versioned docs dir
|
||||
echo "Creating copy of docs directory $DOCS_DIRECTORY/$PREVIOUS_DOCS_VERSION in $DOCS_DIRECTORY/$NEW_DOCS_VERSION"
|
||||
cp -r $DOCS_DIRECTORY/${PREVIOUS_DOCS_VERSION}/ $DOCS_DIRECTORY/${NEW_DOCS_VERSION}/
|
||||
|
||||
# 'git add' the previous version's docs as-is so we get a useful diff when we copy the $MAIN_BRANCH docs in
|
||||
echo "Running 'git add' for previous version's doc contents to use as a base for diff"
|
||||
git add -f $DOCS_DIRECTORY/${NEW_DOCS_VERSION}
|
||||
|
||||
# now copy the contents of $DOCS_DIRECTORY/$MAIN_BRANCH into the same directory so we can get a nice
|
||||
# git diff of what changed since previous version
|
||||
echo "Copying $DOCS_DIRECTORY/$MAIN_BRANCH/ to $DOCS_DIRECTORY/${NEW_DOCS_VERSION}/"
|
||||
rm -rf $DOCS_DIRECTORY/${NEW_DOCS_VERSION}/ && cp -r $DOCS_DIRECTORY/$MAIN_BRANCH/ $DOCS_DIRECTORY/${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 $DATA_DOCS_DIRECTORY/$PREVIOUS_DOCS_TOC.yml at $DATA_DOCS_DIRECTORY/$NEW_DOCS_TOC.yml"
|
||||
cp $DATA_DOCS_DIRECTORY/$PREVIOUS_DOCS_TOC.yml $DATA_DOCS_DIRECTORY/$NEW_DOCS_TOC.yml
|
||||
|
||||
# 'git add' the previous version's ToC content as-is so we get a useful diff when we copy the $MAIN_BRANCH ToC in
|
||||
echo "Running 'git add' for previous version's ToC to use as a base for diff"
|
||||
git add $DATA_DOCS_DIRECTORY/$NEW_DOCS_TOC.yml
|
||||
|
||||
# now copy the $MAIN_BRANCH ToC so we can get a nice git diff of what changed since previous version
|
||||
echo "Copying $DATA_DOCS_DIRECTORY/$MAIN_BRANCH-toc.yml to $DATA_DOCS_DIRECTORY/$NEW_DOCS_TOC.yml"
|
||||
rm $DATA_DOCS_DIRECTORY/$NEW_DOCS_TOC.yml && cp $DATA_DOCS_DIRECTORY/$MAIN_BRANCH-toc.yml $DATA_DOCS_DIRECTORY/$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 $DOCS_DIRECTORY/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i '' "s|https://velero.io/docs/$MAIN_BRANCH|https://velero.io/docs/$VELERO_VERSION|g"
|
||||
find $DOCS_DIRECTORY/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i '' "s|https://github.com/vmware-tanzu/velero/blob/$MAIN_BRANCH|https://github.com/vmware-tanzu/velero/blob/$VELERO_VERSION|g"
|
||||
find $DOCS_DIRECTORY/${NEW_DOCS_VERSION} -type f -name "_index.md" | xargs sed -i '' "s|version: $MAIN_BRANCH|version: $NEW_DOCS_VERSION|g"
|
||||
|
||||
echo "[OS X] Updating latest version in $CONFIG_FILE"
|
||||
sed -i '' "s/latest: ${PREVIOUS_DOCS_VERSION}/latest: ${NEW_DOCS_VERSION}/" $CONFIG_FILE
|
||||
|
||||
# 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_FILE"
|
||||
sed -i '' "/- $MAIN_BRANCH/a\\
|
||||
\ \ \ \ - ${NEW_DOCS_VERSION}
|
||||
" $CONFIG_FILE
|
||||
|
||||
echo "[OS X] Adding ToC mapping entry"
|
||||
sed -i '' "/$MAIN_BRANCH: $MAIN_BRANCH-toc/a\\
|
||||
${NEW_DOCS_VERSION}: ${NEW_DOCS_TOC}
|
||||
" $DATA_DOCS_DIRECTORY/toc-mapping.yml
|
||||
|
||||
else
|
||||
echo "[Linux] updating version-specific links"
|
||||
find $DOCS_DIRECTORY/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i'' "s|https://velero.io/docs/$MAIN_BRANCH|https://velero.io/docs/$VELERO_VERSION|g"
|
||||
find $DOCS_DIRECTORY/${NEW_DOCS_VERSION} -type f -name "*.md" | xargs sed -i'' "s|https://github.com/vmware-tanzu/velero/blob/$MAIN_BRANCH|https://github.com/vmware-tanzu/velero/blob/$VELERO_VERSION|g"
|
||||
|
||||
echo "[Linux] Updating latest version in $CONFIG_FILE"
|
||||
sed -i'' "s/latest: ${PREVIOUS_DOCS_VERSION}/latest: ${NEW_DOCS_VERSION}/" $CONFIG_FILE
|
||||
|
||||
echo "[Linux] Adding latest version to versions list in $CONFIG_FILE"
|
||||
sed -i'' "/- $MAIN_BRANCH/a - ${NEW_DOCS_VERSION}" $CONFIG_FILE
|
||||
|
||||
echo "[Linux] Adding ToC mapping entry"
|
||||
sed -i'' "/$MAIN_BRANCH: $MAIN_BRANCH-toc/a ${NEW_DOCS_VERSION}: ${NEW_DOCS_TOC}" $DATA_DOCS_DIRECTORY/toc-mapping.yml
|
||||
fi
|
||||
|
||||
echo "Success! $DOCS_DIRECTORY/$NEW_DOCS_VERSION has been created."
|
||||
echo ""
|
||||
echo "The next steps are:"
|
||||
echo " 1. Consult site/README-HUGO.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'."
|
||||
@@ -36,14 +36,16 @@ else
|
||||
export GIT_TREE_STATE=dirty
|
||||
fi
|
||||
|
||||
# $PUBLISH must explicitly be set to 'true' for goreleaser
|
||||
# $PUBLISH must explicitly be set to 'TRUE' for goreleaser
|
||||
# to publish the release to GitHub.
|
||||
if [[ "${PUBLISH:-}" != "true" ]]; then
|
||||
if [[ "${PUBLISH:-}" != "TRUE" ]]; then
|
||||
echo "Not set to publish"
|
||||
goreleaser release \
|
||||
--rm-dist \
|
||||
--release-notes="${RELEASE_NOTES_FILE}" \
|
||||
--skip-publish
|
||||
else
|
||||
echo "Getting ready to publish"
|
||||
goreleaser release \
|
||||
--rm-dist \
|
||||
--release-notes="${RELEASE_NOTES_FILE}"
|
||||
@@ -15,9 +15,24 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
# This script will do the necessary checks and actions to create a release of Velero.
|
||||
# It will first validate that all prerequisites are met, then verify the version string is what the user expects.
|
||||
# A git tag will be created and pushed to GitHub, and GoReleaser will be invoked.
|
||||
# This script will do the necessary checks and actions to create a release of Velero. It will:
|
||||
# - validate that all prerequisites are met
|
||||
# - verify the version string is what the user expects.
|
||||
# - create a git tag
|
||||
# - push the created git tag to GitHub
|
||||
# - run GoReleaser
|
||||
|
||||
# The following variables are needed:
|
||||
|
||||
# - $VELERO_VERSION: defines the tag of Velero that any https://github.com/vmware-tanzu/velero/...
|
||||
# links in the docs should redirect to.
|
||||
# - $publish: TRUE/FALSE value where FALSE (or not including it) will indicate a dry-run, and TRUE, or simply adding 'publish',
|
||||
# will tag the release with the $VELERO_VERSION and push the tag to a remote named 'upstream'.
|
||||
# - $GITHUB_TOKEN: Needed to run the goreleaser process to generate a GitHub release.
|
||||
# Use https://github.com/settings/tokens/new?scopes=repo if you don't already have a token.
|
||||
# Regenerate an existing token: https://github.com/settings/tokens.
|
||||
# You may regenerate the token for every release if you prefer.
|
||||
# See https://goreleaser.com/environment/ for more details.
|
||||
|
||||
# This script is meant to be a combination of documentation and executable.
|
||||
# If you have questions at any point, please stop and ask!
|
||||
@@ -25,12 +40,25 @@
|
||||
# Directory in which the script itself resides, so we can use it for calling programs that are in the same directory.
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
|
||||
# Parse out the branch we're on so we can switch back to it at the end of a dry-run, where we delete the tag. Requires git v1.8.1+
|
||||
upstream_branch=$(git symbolic-ref --short HEAD)
|
||||
|
||||
function tag_and_push() {
|
||||
echo "Tagging and pushing $VELERO_VERSION"
|
||||
git tag $VELERO_VERSION
|
||||
git push $VELERO_VERSION
|
||||
echo "Tagging $VELERO_VERSION"
|
||||
git tag $VELERO_VERSION || true
|
||||
|
||||
if [[ $publish == "TRUE" ]]; then
|
||||
echo "Pushing $VELERO_VERSION"
|
||||
git push upstream $VELERO_VERSION
|
||||
fi
|
||||
}
|
||||
|
||||
# Default to a dry-run mode
|
||||
publish=FALSE
|
||||
if [[ "$1" = "publish" ]]; then
|
||||
publish=TRUE
|
||||
fi
|
||||
|
||||
# For now, have the person doing the release pass in the VELERO_VERSION variable as an environment variable.
|
||||
# In the future, we might be able to inspect git via `git describe --abbrev=0` to get a hint for it.
|
||||
if [[ -z "$VELERO_VERSION" ]]; then
|
||||
@@ -44,7 +72,7 @@ if [[ -z "$GITHUB_TOKEN" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Ensure that we have a clean working tree before we let any changes happen, especially important for cutting release branches.
|
||||
# Ensure that we have a clean working tree before we let any changes happen, especially important for cutting release branches.
|
||||
if [[ -n $(git status --short) ]]; then
|
||||
echo "Your git working directory is dirty! Please clean up untracked files and stash any changes before proceeding."
|
||||
exit 3
|
||||
@@ -63,13 +91,19 @@ printf "Based on this, the following assumptions have been made: \n"
|
||||
|
||||
[[ "$VELERO_PATCH" != 0 ]] && printf "*\t This is a patch release.\n"
|
||||
|
||||
# $VELERO_PRERELEASE gets populated by the chk_version.go script that parses and verifies the given version format
|
||||
# -n is "string is non-empty"
|
||||
[[ -n $VELERO_PRERELEASE ]] && printf "*\t This is a pre-release.\n"
|
||||
|
||||
# -z is "string is empty"
|
||||
[[ -z $VELERO_PRERELEASE ]] && printf "*\t This is a GA release.\n"
|
||||
|
||||
echo "If this is all correct, press enter/return to proceed to TAG THE RELEASE and UPLOAD THE TAG TO GITHUB."
|
||||
if [[ $publish == "TRUE" ]]; then
|
||||
echo "If this is all correct, press enter/return to proceed to TAG THE RELEASE and UPLOAD THE TAG TO GITHUB."
|
||||
else
|
||||
echo "If this is all correct, press enter/return to proceed to TAG THE RELEASE and PROCEED WITH THE DRY-RUN."
|
||||
fi
|
||||
|
||||
echo "Otherwise, press ctrl-c to CANCEL the process without making any changes."
|
||||
|
||||
read -p "Ready to continue? "
|
||||
@@ -77,8 +111,9 @@ read -p "Ready to continue? "
|
||||
echo "Alright, let's go."
|
||||
|
||||
echo "Pulling down all git tags and branches before doing any work."
|
||||
git fetch upstream --all --tags
|
||||
git fetch upstream --tags
|
||||
|
||||
# $VELERO_PATCH gets populated by the chk_version.go scrip that parses and verifies the given version format
|
||||
# If we've got a patch release, we'll need to create a release branch for it.
|
||||
if [[ "$VELERO_PATCH" > 0 ]]; then
|
||||
release_branch_name=release-$VELERO_MAJOR.$VELERO_MINOR
|
||||
@@ -95,24 +130,38 @@ if [[ "$VELERO_PATCH" > 0 ]]; then
|
||||
|
||||
echo "Now you'll need to cherry-pick any relevant git commits into this release branch."
|
||||
echo "Either pause this script with ctrl-z, or open a new terminal window and do the cherry-picking."
|
||||
read -p "Press enter when you're done cherry-picking. THIS WILL MAKE A TAG PUSH THE BRANCH TO UPSTREAM"
|
||||
if [[ $publish == "TRUE" ]]; then
|
||||
read -p "Press enter when you're done cherry-picking. THIS WILL MAKE A TAG PUSH THE BRANCH TO upstream"
|
||||
else
|
||||
read -p "Press enter when you're done cherry-picking."
|
||||
fi
|
||||
|
||||
# TODO can/should we add a way to review the cherry-picked commits before the push?
|
||||
|
||||
echo "Pushing $release_branch_name to upstream remote"
|
||||
git push --set-upstream upstream/$release_branch_name $release_branch_name
|
||||
if [[ $publish == "TRUE" ]]; then
|
||||
echo "Pushing $release_branch_name to upstream remote"
|
||||
git push --set-upstream upstream/$release_branch_name $release_branch_name
|
||||
fi
|
||||
|
||||
tag_and_push
|
||||
else
|
||||
echo "Checking out upstream/master."
|
||||
git checkout upstream/master
|
||||
echo "Checking out upstream/main."
|
||||
git checkout upstream/main
|
||||
|
||||
tag_and_push
|
||||
fi
|
||||
|
||||
|
||||
|
||||
echo "Invoking Goreleaser to create the GitHub release."
|
||||
RELEASE_NOTES_FILE=changelogs/CHANGELOG-$VELERO_MAJOR.$VELERO_MINOR.md \
|
||||
PUBLISH=TRUE \
|
||||
PUBLISH=$publish \
|
||||
make release
|
||||
|
||||
if [[ $publish == "FALSE" ]]; then
|
||||
# Delete the local tag so we don't potentially conflict when it's re-run for real.
|
||||
# This also means we won't have to just ignore existing tags in tag_and_push, which could be a problem if there's an existing tag.
|
||||
echo "Dry run complete. Deleting git tag $VELERO_VERSION"
|
||||
git checkout $upstream_branch
|
||||
git tag -d $VELERO_VERSION
|
||||
fi
|
||||
|
||||
|
||||
3
hack/restore-crd-patch.json
Normal file
3
hack/restore-crd-patch.json
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
{ "op": "replace", "path": "/spec/validation/openAPIV3Schema/properties/spec/properties/hooks/properties/resources/items/properties/postHooks/items/properties/init/properties/initContainers/items/properties/ports/items/required", "value": [ "containerPort", "protocol"] }
|
||||
]
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2016 The Kubernetes Authors.
|
||||
# Modifications Copyright 2020 The Velero Contributors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +24,7 @@ export CGO_ENABLED=0
|
||||
TARGETS=(
|
||||
./cmd/...
|
||||
./pkg/...
|
||||
./internal/...
|
||||
)
|
||||
|
||||
if [[ ${#@} -ne 0 ]]; then
|
||||
|
||||
@@ -52,4 +52,13 @@ controller-gen \
|
||||
paths=./pkg/controller/... \
|
||||
output:crd:artifacts:config=config/crd/bases
|
||||
|
||||
# this is a super hacky workaround for https://github.com/kubernetes/kubernetes/issues/91395
|
||||
# which a result of fixing the validation on CRD objects. The validation ensures the fields that are list map keys, are either marked
|
||||
# as required or have default values to ensure merging of list map items work as expected.
|
||||
# With "containerPort" and "protocol" being considered as x-kubernetes-list-map-keys in the container ports, and "protocol" was not
|
||||
# a required field, the CRD would fail validation with errors similar to the one reported in https://github.com/kubernetes/kubernetes/issues/91395.
|
||||
# once controller-gen (above) is able to generate CRDs with `protocol` as a required field, this hack can be removed.
|
||||
kubectl patch -f config/crd/bases/velero.io_restores.yaml -p "$(cat hack/restore-crd-patch.json)" --type=json --local=true -o yaml > /tmp/velero.io_restores-yaml.patched
|
||||
mv /tmp/velero.io_restores-yaml.patched config/crd/bases/velero.io_restores.yaml
|
||||
|
||||
go generate ./config/crd/crds
|
||||
|
||||
@@ -22,8 +22,8 @@ ${HACK_DIR}/update-generated-crd-code.sh --verify-only
|
||||
if ! git diff --exit-code config/crd/crds/crds.go >/dev/null; then
|
||||
# revert changes to state before running CRD generation to stay consistent
|
||||
# with code-generator `--verify-only` option which discards generated changes
|
||||
git checkout config/crd/bases
|
||||
git checkout config/crd
|
||||
|
||||
echo "CRD verification - failed! Generated CRDs are out-of-date, please run 'make update'."
|
||||
echo "CRD verification - failed! Generated CRDs are out-of-date, please run 'make update' and 'git add' the generated file(s)."
|
||||
exit 1
|
||||
fi
|
||||
199
internal/delete/delete_item_action_handler.go
Normal file
199
internal/delete/delete_item_action_handler.go
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package delete
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/archive"
|
||||
"github.com/vmware-tanzu/velero/pkg/discovery"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/collections"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
|
||||
)
|
||||
|
||||
// Context provides the necessary environment to run DeleteItemAction plugins
|
||||
type Context struct {
|
||||
Backup *velerov1api.Backup
|
||||
BackupReader io.Reader
|
||||
Actions []velero.DeleteItemAction
|
||||
Filesystem filesystem.Interface
|
||||
Log logrus.FieldLogger
|
||||
DiscoveryHelper discovery.Helper
|
||||
|
||||
resolvedActions []resolvedAction
|
||||
}
|
||||
|
||||
func InvokeDeleteActions(ctx *Context) error {
|
||||
var err error
|
||||
ctx.resolvedActions, err = resolveActions(ctx.Actions, ctx.DiscoveryHelper)
|
||||
|
||||
// No actions installed and no error means we don't have to continue;
|
||||
// just do the backup deletion without worrying about plugins.
|
||||
if len(ctx.resolvedActions) == 0 && err == nil {
|
||||
ctx.Log.Debug("No delete item actions present, proceeding with rest of backup deletion process")
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return errors.Wrapf(err, "error resolving actions")
|
||||
}
|
||||
|
||||
// get items out of backup tarball into a temp directory
|
||||
dir, err := archive.NewExtractor(ctx.Log, ctx.Filesystem).UnzipAndExtractBackup(ctx.BackupReader)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error extracting backup")
|
||||
|
||||
}
|
||||
defer ctx.Filesystem.RemoveAll(dir)
|
||||
ctx.Log.Debugf("Downloaded and extracted the backup file to: %s", dir)
|
||||
|
||||
backupResources, err := archive.NewParser(ctx.Log, ctx.Filesystem).Parse(dir)
|
||||
processdResources := sets.NewString()
|
||||
|
||||
ctx.Log.Debugf("Trying to reconcile resource names with Kube API server.")
|
||||
// Transform resource names based on what's canonical in the API server.
|
||||
for resource := range backupResources {
|
||||
gvr, _, err := ctx.DiscoveryHelper.ResourceFor(schema.ParseGroupResource(resource).WithVersion(""))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to resolve resource into complete group/version/resource: %v", resource)
|
||||
}
|
||||
|
||||
groupResource := gvr.GroupResource()
|
||||
|
||||
// We've already seen this group/resource, so don't process it again.
|
||||
if processdResources.Has(groupResource.String()) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get a list of all items that exist for this resource
|
||||
resourceList := backupResources[groupResource.String()]
|
||||
if resourceList == nil {
|
||||
// After canonicalization from the API server, the resources may not exist in the tarball
|
||||
// Skip them if that's the case.
|
||||
continue
|
||||
}
|
||||
|
||||
// Iterate over all items, grouped by namespace.
|
||||
for namespace, items := range resourceList.ItemsByNamespace {
|
||||
nsLog := ctx.Log.WithField("namespace", namespace)
|
||||
nsLog.Info("Starting to check for items in namespace")
|
||||
|
||||
// Filter applicable actions based on namespace only once per namespace.
|
||||
actions := ctx.getApplicableActions(groupResource, namespace)
|
||||
|
||||
// Process individual items from the backup
|
||||
for _, item := range items {
|
||||
itemPath := archive.GetItemFilePath(dir, resource, namespace, item)
|
||||
|
||||
// obj is the Unstructured item from the backup
|
||||
obj, err := archive.Unmarshal(ctx.Filesystem, itemPath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Could not unmarshal item: %v", item)
|
||||
}
|
||||
|
||||
itemLog := nsLog.WithField("item", obj.GetName())
|
||||
itemLog.Infof("invoking DeleteItemAction plugins")
|
||||
|
||||
for _, action := range actions {
|
||||
if !action.selector.Matches(labels.Set(obj.GetLabels())) {
|
||||
continue
|
||||
}
|
||||
err = action.Execute(&velero.DeleteItemActionExecuteInput{
|
||||
Item: obj,
|
||||
Backup: ctx.Backup,
|
||||
})
|
||||
// Since we want to keep looping even on errors, log them instead of just returning.
|
||||
if err != nil {
|
||||
itemLog.WithError(err).Error("plugin error")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getApplicableActions takes resolved DeleteItemActions and filters them for a given group/resource and namespace.
|
||||
func (ctx *Context) getApplicableActions(groupResource schema.GroupResource, namespace string) []resolvedAction {
|
||||
var actions []resolvedAction
|
||||
|
||||
for _, action := range ctx.resolvedActions {
|
||||
if !action.resourceIncludesExcludes.ShouldInclude(groupResource.String()) {
|
||||
continue
|
||||
}
|
||||
|
||||
if namespace != "" && !action.namespaceIncludesExcludes.ShouldInclude(namespace) {
|
||||
continue
|
||||
}
|
||||
|
||||
if namespace == "" && !action.namespaceIncludesExcludes.IncludeEverything() {
|
||||
continue
|
||||
}
|
||||
|
||||
actions = append(actions, action)
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
// resolvedActions are DeleteItemActions decorated with resource/namespace include/exclude collections, as well as label selectors for easy comparison.
|
||||
type resolvedAction struct {
|
||||
velero.DeleteItemAction
|
||||
|
||||
resourceIncludesExcludes *collections.IncludesExcludes
|
||||
namespaceIncludesExcludes *collections.IncludesExcludes
|
||||
selector labels.Selector
|
||||
}
|
||||
|
||||
// resolveActions resolves the AppliesTo ResourceSelectors of DeleteItemActions plugins against the Kubernetes discovery API for fully-qualified names.
|
||||
func resolveActions(actions []velero.DeleteItemAction, helper discovery.Helper) ([]resolvedAction, error) {
|
||||
var resolved []resolvedAction
|
||||
|
||||
for _, action := range actions {
|
||||
resourceSelector, err := action.AppliesTo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resources := collections.GetResourceIncludesExcludes(helper, resourceSelector.IncludedResources, resourceSelector.ExcludedResources)
|
||||
namespaces := collections.NewIncludesExcludes().Includes(resourceSelector.IncludedNamespaces...).Excludes(resourceSelector.ExcludedNamespaces...)
|
||||
|
||||
selector := labels.Everything()
|
||||
if resourceSelector.LabelSelector != "" {
|
||||
if selector, err = labels.Parse(resourceSelector.LabelSelector); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
res := resolvedAction{
|
||||
DeleteItemAction: action,
|
||||
resourceIncludesExcludes: resources,
|
||||
namespaceIncludesExcludes: namespaces,
|
||||
selector: selector,
|
||||
}
|
||||
resolved = append(resolved, res)
|
||||
}
|
||||
|
||||
return resolved, nil
|
||||
}
|
||||
268
internal/delete/delete_item_action_handler_test.go
Normal file
268
internal/delete/delete_item_action_handler_test.go
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package delete
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"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/runtime"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
"github.com/vmware-tanzu/velero/pkg/discovery"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/test"
|
||||
kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
)
|
||||
|
||||
func TestInvokeDeleteItemActionsRunForCorrectItems(t *testing.T) {
|
||||
// Declare test-singleton objects.
|
||||
fs := test.NewFakeFileSystem()
|
||||
log := logrus.StandardLogger()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
backup *velerov1api.Backup
|
||||
apiResources []*test.APIResource
|
||||
tarball io.Reader
|
||||
actions map[*recordResourcesAction][]string // recordResourceActions are the plugins that will capture item ids, the []string values are the ids we'll test against.
|
||||
}{
|
||||
{
|
||||
name: "single action with no selector runs for all items",
|
||||
backup: builder.ForBackup("velero", "velero").Result(),
|
||||
tarball: test.NewTarWriter(t).
|
||||
AddItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()).
|
||||
AddItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result(), builder.ForPersistentVolume("pv-2").Result()).
|
||||
Done(),
|
||||
apiResources: []*test.APIResource{test.Pods(), test.PVs()},
|
||||
actions: map[*recordResourcesAction][]string{
|
||||
new(recordResourcesAction): {"ns-1/pod-1", "ns-2/pod-2", "pv-1", "pv-2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "single action with a resource selector for namespaced resources runs only for matching resources",
|
||||
backup: builder.ForBackup("velero", "velero").Result(),
|
||||
tarball: test.NewTarWriter(t).
|
||||
AddItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()).
|
||||
AddItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result(), builder.ForPersistentVolume("pv-2").Result()).
|
||||
Done(),
|
||||
apiResources: []*test.APIResource{test.Pods(), test.PVs()},
|
||||
actions: map[*recordResourcesAction][]string{
|
||||
new(recordResourcesAction).ForResource("pods"): {"ns-1/pod-1", "ns-2/pod-2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "single action with a resource selector for cluster-scoped resources runs only for matching resources",
|
||||
backup: builder.ForBackup("velero", "velero").Result(),
|
||||
tarball: test.NewTarWriter(t).
|
||||
AddItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()).
|
||||
AddItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result(), builder.ForPersistentVolume("pv-2").Result()).
|
||||
Done(),
|
||||
apiResources: []*test.APIResource{test.Pods(), test.PVs()},
|
||||
actions: map[*recordResourcesAction][]string{
|
||||
new(recordResourcesAction).ForResource("persistentvolumes"): {"pv-1", "pv-2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "single action with a namespace selector runs only for resources in that namespace",
|
||||
backup: builder.ForBackup("velero", "velero").Result(),
|
||||
tarball: test.NewTarWriter(t).
|
||||
AddItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()).
|
||||
AddItems("persistentvolumeclaims", builder.ForPersistentVolumeClaim("ns-1", "pvc-1").Result(), builder.ForPersistentVolumeClaim("ns-2", "pvc-2").Result()).
|
||||
AddItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result(), builder.ForPersistentVolume("pv-2").Result()).
|
||||
Done(),
|
||||
apiResources: []*test.APIResource{test.Pods(), test.PVCs(), test.PVs()},
|
||||
actions: map[*recordResourcesAction][]string{
|
||||
new(recordResourcesAction).ForNamespace("ns-1"): {"ns-1/pod-1", "ns-1/pvc-1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple actions, each with a different resource selector using short name, run for matching resources",
|
||||
backup: builder.ForBackup("velero", "velero").Result(),
|
||||
tarball: test.NewTarWriter(t).
|
||||
AddItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()).
|
||||
AddItems("persistentvolumeclaims", builder.ForPersistentVolumeClaim("ns-1", "pvc-1").Result(), builder.ForPersistentVolumeClaim("ns-2", "pvc-2").Result()).
|
||||
AddItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result(), builder.ForPersistentVolume("pv-2").Result()).
|
||||
Done(),
|
||||
apiResources: []*test.APIResource{test.Pods(), test.PVCs(), test.PVs()},
|
||||
actions: map[*recordResourcesAction][]string{
|
||||
new(recordResourcesAction).ForResource("po"): {"ns-1/pod-1", "ns-2/pod-2"},
|
||||
new(recordResourcesAction).ForResource("pv"): {"pv-1", "pv-2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "actions with selectors that don't match anything don't run for any resources",
|
||||
backup: builder.ForBackup("velero", "velero").Result(),
|
||||
tarball: test.NewTarWriter(t).
|
||||
AddItems("pods", builder.ForPod("ns-1", "pod-1").Result()).
|
||||
AddItems("persistentvolumeclaims", builder.ForPersistentVolumeClaim("ns-2", "pvc-2").Result()).
|
||||
Done(),
|
||||
apiResources: []*test.APIResource{test.Pods(), test.PVCs(), test.PVs()},
|
||||
actions: map[*recordResourcesAction][]string{
|
||||
new(recordResourcesAction).ForNamespace("ns-1").ForResource("persistentvolumeclaims"): nil,
|
||||
new(recordResourcesAction).ForNamespace("ns-2").ForResource("pods"): nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "single action with label selector runs only for those items",
|
||||
backup: builder.ForBackup("velero", "velero").Result(),
|
||||
tarball: test.NewTarWriter(t).
|
||||
AddItems("pods", builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("app", "app1")).Result(), builder.ForPod("ns-2", "pod-2").Result()).
|
||||
AddItems("persistentvolumeclaims", builder.ForPersistentVolumeClaim("ns-1", "pvc-1").Result(), builder.ForPersistentVolumeClaim("ns-2", "pvc-2").ObjectMeta(builder.WithLabels("app", "app1")).Result()).
|
||||
Done(),
|
||||
apiResources: []*test.APIResource{test.Pods(), test.PVCs()},
|
||||
actions: map[*recordResourcesAction][]string{
|
||||
new(recordResourcesAction).ForLabelSelector("app=app1"): {"ns-1/pod-1", "ns-2/pvc-2"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// test harness contains the fake API server/discovery client
|
||||
h := newHarness(t)
|
||||
for _, r := range tc.apiResources {
|
||||
h.addResource(t, r)
|
||||
}
|
||||
|
||||
// Get the plugins out of the map in order to use them.
|
||||
actions := []velero.DeleteItemAction{}
|
||||
for action := range tc.actions {
|
||||
actions = append(actions, action)
|
||||
}
|
||||
|
||||
c := &Context{
|
||||
Backup: tc.backup,
|
||||
BackupReader: tc.tarball,
|
||||
Filesystem: fs,
|
||||
DiscoveryHelper: h.discoveryHelper,
|
||||
Actions: actions,
|
||||
Log: log,
|
||||
}
|
||||
|
||||
err := InvokeDeleteActions(c)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Compare the plugins against the ids that we wanted.
|
||||
for action, want := range tc.actions {
|
||||
sort.Strings(want)
|
||||
sort.Strings(action.ids)
|
||||
assert.Equal(t, want, action.ids)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: unify this with the test harness in pkg/restore/restore_test.go
|
||||
type harness struct {
|
||||
*test.APIServer
|
||||
discoveryHelper discovery.Helper
|
||||
}
|
||||
|
||||
func newHarness(t *testing.T) *harness {
|
||||
t.Helper()
|
||||
|
||||
apiServer := test.NewAPIServer(t)
|
||||
log := logrus.StandardLogger()
|
||||
|
||||
discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, log)
|
||||
require.NoError(t, err)
|
||||
|
||||
return &harness{
|
||||
APIServer: apiServer,
|
||||
discoveryHelper: discoveryHelper,
|
||||
}
|
||||
}
|
||||
|
||||
// addResource adds an APIResource and it's items to a faked API server for testing.
|
||||
func (h *harness) addResource(t *testing.T, resource *test.APIResource) {
|
||||
t.Helper()
|
||||
|
||||
h.DiscoveryClient.WithAPIResource(resource)
|
||||
require.NoError(t, h.discoveryHelper.Refresh())
|
||||
|
||||
for _, item := range resource.Items {
|
||||
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(item)
|
||||
require.NoError(t, err)
|
||||
|
||||
unstructuredObj := &unstructured.Unstructured{Object: obj}
|
||||
if resource.Namespaced {
|
||||
_, err = h.DynamicClient.Resource(resource.GVR()).Namespace(item.GetNamespace()).Create(context.TODO(), unstructuredObj, metav1.CreateOptions{})
|
||||
} else {
|
||||
_, err = h.DynamicClient.Resource(resource.GVR()).Create(context.TODO(), unstructuredObj, metav1.CreateOptions{})
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
// recordResourcesAction is a delete item action that can be configured to run
|
||||
// for specific resources/namespaces and simply record the items that is is
|
||||
// executed for.
|
||||
type recordResourcesAction struct {
|
||||
selector velero.ResourceSelector
|
||||
ids []string
|
||||
}
|
||||
|
||||
func (a *recordResourcesAction) AppliesTo() (velero.ResourceSelector, error) {
|
||||
return a.selector, nil
|
||||
}
|
||||
|
||||
func (a *recordResourcesAction) Execute(input *velero.DeleteItemActionExecuteInput) error {
|
||||
metadata, err := meta.Accessor(input.Item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.ids = append(a.ids, kubeutil.NamespaceAndName(metadata))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *recordResourcesAction) ForResource(resource string) *recordResourcesAction {
|
||||
a.selector.IncludedResources = append(a.selector.IncludedResources, resource)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *recordResourcesAction) ForNamespace(namespace string) *recordResourcesAction {
|
||||
a.selector.IncludedNamespaces = append(a.selector.IncludedNamespaces, namespace)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *recordResourcesAction) ForLabelSelector(selector string) *recordResourcesAction {
|
||||
a.selector.LabelSelector = selector
|
||||
return a
|
||||
}
|
||||
|
||||
func TestInvokeDeleteItemActionsWithNoPlugins(t *testing.T) {
|
||||
c := &Context{
|
||||
Backup: builder.ForBackup("velero", "velero").Result(),
|
||||
Log: logrus.StandardLogger(),
|
||||
// No other fields are set on the assumption that if 0 actions are present,
|
||||
// the backup tarball and file system being empty will produce no errors.
|
||||
}
|
||||
err := InvokeDeleteActions(c)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
520
internal/hook/item_hook_handler.go
Normal file
520
internal/hook/item_hook_handler.go
Normal file
@@ -0,0 +1,520 @@
|
||||
/*
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package hook
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
uuid "github.com/gofrs/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
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"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
||||
"github.com/vmware-tanzu/velero/pkg/podexec"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/collections"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
)
|
||||
|
||||
type hookPhase string
|
||||
|
||||
const (
|
||||
PhasePre hookPhase = "pre"
|
||||
PhasePost hookPhase = "post"
|
||||
)
|
||||
|
||||
const (
|
||||
// Backup hook annotations
|
||||
podBackupHookContainerAnnotationKey = "hook.backup.velero.io/container"
|
||||
podBackupHookCommandAnnotationKey = "hook.backup.velero.io/command"
|
||||
podBackupHookOnErrorAnnotationKey = "hook.backup.velero.io/on-error"
|
||||
podBackupHookTimeoutAnnotationKey = "hook.backup.velero.io/timeout"
|
||||
|
||||
// Restore hook annotations
|
||||
podRestoreHookContainerAnnotationKey = "post.hook.restore.velero.io/container"
|
||||
podRestoreHookCommandAnnotationKey = "post.hook.restore.velero.io/command"
|
||||
podRestoreHookOnErrorAnnotationKey = "post.hook.restore.velero.io/on-error"
|
||||
podRestoreHookTimeoutAnnotationKey = "post.hook.restore.velero.io/exec-timeout"
|
||||
podRestoreHookWaitTimeoutAnnotationKey = "post.hook.restore.velero.io/wait-timeout"
|
||||
podRestoreHookInitContainerImageAnnotationKey = "init.hook.restore.velero.io/container-image"
|
||||
podRestoreHookInitContainerNameAnnotationKey = "init.hook.restore.velero.io/container-name"
|
||||
podRestoreHookInitContainerCommandAnnotationKey = "init.hook.restore.velero.io/command"
|
||||
podRestoreHookInitContainerTimeoutAnnotationKey = "init.hook.restore.velero.io/timeout"
|
||||
)
|
||||
|
||||
// ItemHookHandler invokes hooks for an item.
|
||||
type ItemHookHandler interface {
|
||||
// HandleHooks invokes hooks for an item. If the item is a pod and the appropriate annotations exist
|
||||
// to specify a hook, that is executed. Otherwise, this looks at the backup context's Backup to
|
||||
// determine if there are any hooks relevant to the item, taking into account the hook spec's
|
||||
// namespaces, resources, and label selector.
|
||||
HandleHooks(
|
||||
log logrus.FieldLogger,
|
||||
groupResource schema.GroupResource,
|
||||
obj runtime.Unstructured,
|
||||
resourceHooks []ResourceHook,
|
||||
phase hookPhase,
|
||||
) error
|
||||
}
|
||||
|
||||
// ItemRestoreHookHandler invokes restore hooks for an item
|
||||
type ItemRestoreHookHandler interface {
|
||||
HandleRestoreHooks(
|
||||
log logrus.FieldLogger,
|
||||
groupResource schema.GroupResource,
|
||||
obj runtime.Unstructured,
|
||||
rh []ResourceRestoreHook,
|
||||
) (runtime.Unstructured, error)
|
||||
}
|
||||
|
||||
// InitContainerRestoreHookHandler is the restore hook handler to add init containers to restored pods.
|
||||
type InitContainerRestoreHookHandler struct{}
|
||||
|
||||
// HandleRestoreHooks runs the restore hooks for an item.
|
||||
// If the item is a pod, then hooks are chosen to be run as follows:
|
||||
// If the pod has the appropriate annotations specifying the hook action, then hooks from the annotation are run
|
||||
// Otherwise, the supplied ResourceRestoreHooks are applied.
|
||||
func (i *InitContainerRestoreHookHandler) HandleRestoreHooks(
|
||||
log logrus.FieldLogger,
|
||||
groupResource schema.GroupResource,
|
||||
obj runtime.Unstructured,
|
||||
resourceRestoreHooks []ResourceRestoreHook,
|
||||
) (runtime.Unstructured, error) {
|
||||
// We only support hooks on pods right now
|
||||
if groupResource != kuberesource.Pods {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
metadata, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to get a metadata accessor")
|
||||
}
|
||||
pod := new(corev1api.Pod)
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), pod); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
initContainers := []corev1api.Container{}
|
||||
// If this pod had pod volumes backed up using restic, then we want to the pod volumes restored prior to
|
||||
// running the restore hook init containers. This allows the restore hook init containers to prepare the
|
||||
// restored data to be consumed by the application container(s).
|
||||
// So if there is a "resitc-wait" init container already on the pod at index 0, we'll preserve that and run
|
||||
// it before running any other init container.
|
||||
if len(pod.Spec.InitContainers) > 0 && pod.Spec.InitContainers[0].Name == restic.InitContainer {
|
||||
initContainers = append(initContainers, pod.Spec.InitContainers[0])
|
||||
pod.Spec.InitContainers = pod.Spec.InitContainers[1:]
|
||||
}
|
||||
|
||||
hooksFromAnnotations := getInitRestoreHookFromAnnotation(kube.NamespaceAndName(pod), metadata.GetAnnotations(), log)
|
||||
if hooksFromAnnotations != nil {
|
||||
log.Infof("Handling InitRestoreHooks from pod annotaions")
|
||||
initContainers = append(initContainers, hooksFromAnnotations.InitContainers...)
|
||||
} else {
|
||||
log.Infof("Handling InitRestoreHooks from RestoreSpec")
|
||||
// pod did not have the annotations appropriate for restore hooks
|
||||
// running applicable ResourceRestoreHooks supplied.
|
||||
namespace := metadata.GetNamespace()
|
||||
labels := labels.Set(metadata.GetLabels())
|
||||
|
||||
for _, rh := range resourceRestoreHooks {
|
||||
if !rh.Selector.applicableTo(groupResource, namespace, labels) {
|
||||
continue
|
||||
}
|
||||
for _, hook := range rh.RestoreHooks {
|
||||
if hook.Init != nil {
|
||||
initContainers = append(initContainers, hook.Init.InitContainers...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pod.Spec.InitContainers = append(initContainers, pod.Spec.InitContainers...)
|
||||
log.Infof("Returning pod %s/%s with %d init container(s)", pod.Namespace, pod.Name, len(pod.Spec.InitContainers))
|
||||
|
||||
podMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&pod)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
return &unstructured.Unstructured{Object: podMap}, nil
|
||||
}
|
||||
|
||||
// DefaultItemHookHandler is the default itemHookHandler.
|
||||
type DefaultItemHookHandler struct {
|
||||
PodCommandExecutor podexec.PodCommandExecutor
|
||||
}
|
||||
|
||||
func (h *DefaultItemHookHandler) HandleHooks(
|
||||
log logrus.FieldLogger,
|
||||
groupResource schema.GroupResource,
|
||||
obj runtime.Unstructured,
|
||||
resourceHooks []ResourceHook,
|
||||
phase hookPhase,
|
||||
) error {
|
||||
// We only support hooks on pods right now
|
||||
if groupResource != kuberesource.Pods {
|
||||
return nil
|
||||
}
|
||||
|
||||
metadata, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to get a metadata accessor")
|
||||
}
|
||||
|
||||
namespace := metadata.GetNamespace()
|
||||
name := metadata.GetName()
|
||||
|
||||
// If the pod has the hook specified via annotations, that takes priority.
|
||||
hookFromAnnotations := getPodExecHookFromAnnotations(metadata.GetAnnotations(), phase, log)
|
||||
if phase == PhasePre && hookFromAnnotations == nil {
|
||||
// See if the pod has the legacy hook annotation keys (i.e. without a phase specified)
|
||||
hookFromAnnotations = getPodExecHookFromAnnotations(metadata.GetAnnotations(), "", log)
|
||||
}
|
||||
if hookFromAnnotations != nil {
|
||||
hookLog := log.WithFields(
|
||||
logrus.Fields{
|
||||
"hookSource": "annotation",
|
||||
"hookType": "exec",
|
||||
"hookPhase": phase,
|
||||
},
|
||||
)
|
||||
if err := h.PodCommandExecutor.ExecutePodCommand(hookLog, obj.UnstructuredContent(), namespace, name, "<from-annotation>", hookFromAnnotations); err != nil {
|
||||
hookLog.WithError(err).Error("Error executing hook")
|
||||
if hookFromAnnotations.OnError == velerov1api.HookErrorModeFail {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
labels := labels.Set(metadata.GetLabels())
|
||||
// Otherwise, check for hooks defined in the backup spec.
|
||||
for _, resourceHook := range resourceHooks {
|
||||
if !resourceHook.Selector.applicableTo(groupResource, namespace, labels) {
|
||||
continue
|
||||
}
|
||||
|
||||
var hooks []velerov1api.BackupResourceHook
|
||||
if phase == PhasePre {
|
||||
hooks = resourceHook.Pre
|
||||
} else {
|
||||
hooks = resourceHook.Post
|
||||
}
|
||||
for _, hook := range hooks {
|
||||
if groupResource == kuberesource.Pods {
|
||||
if hook.Exec != nil {
|
||||
hookLog := log.WithFields(
|
||||
logrus.Fields{
|
||||
"hookSource": "backupSpec",
|
||||
"hookType": "exec",
|
||||
"hookPhase": phase,
|
||||
},
|
||||
)
|
||||
err := h.PodCommandExecutor.ExecutePodCommand(hookLog, obj.UnstructuredContent(), namespace, name, resourceHook.Name, hook.Exec)
|
||||
if err != nil {
|
||||
hookLog.WithError(err).Error("Error executing hook")
|
||||
if hook.Exec.OnError == velerov1api.HookErrorModeFail {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func phasedKey(phase hookPhase, key string) string {
|
||||
if phase != "" {
|
||||
return fmt.Sprintf("%v.%v", phase, key)
|
||||
}
|
||||
return string(key)
|
||||
}
|
||||
|
||||
func getHookAnnotation(annotations map[string]string, key string, phase hookPhase) string {
|
||||
return annotations[phasedKey(phase, key)]
|
||||
}
|
||||
|
||||
// getPodExecHookFromAnnotations returns an ExecHook based on the annotations, as long as the
|
||||
// 'command' annotation is present. If it is absent, this returns nil.
|
||||
// If there is an error in parsing a supplied timeout, it is logged.
|
||||
func getPodExecHookFromAnnotations(annotations map[string]string, phase hookPhase, log logrus.FieldLogger) *velerov1api.ExecHook {
|
||||
commandValue := getHookAnnotation(annotations, podBackupHookCommandAnnotationKey, phase)
|
||||
if commandValue == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
container := getHookAnnotation(annotations, podBackupHookContainerAnnotationKey, phase)
|
||||
|
||||
onError := velerov1api.HookErrorMode(getHookAnnotation(annotations, podBackupHookOnErrorAnnotationKey, phase))
|
||||
if onError != velerov1api.HookErrorModeContinue && onError != velerov1api.HookErrorModeFail {
|
||||
onError = ""
|
||||
}
|
||||
|
||||
var timeout time.Duration
|
||||
timeoutString := getHookAnnotation(annotations, podBackupHookTimeoutAnnotationKey, phase)
|
||||
if timeoutString != "" {
|
||||
if temp, err := time.ParseDuration(timeoutString); err == nil {
|
||||
timeout = temp
|
||||
} else {
|
||||
log.Warn(errors.Wrapf(err, "Unable to parse provided timeout %s, using default", timeoutString))
|
||||
}
|
||||
}
|
||||
|
||||
return &velerov1api.ExecHook{
|
||||
Container: container,
|
||||
Command: parseStringToCommand(commandValue),
|
||||
OnError: onError,
|
||||
Timeout: metav1.Duration{Duration: timeout},
|
||||
}
|
||||
}
|
||||
|
||||
func parseStringToCommand(commandValue string) []string {
|
||||
var command []string
|
||||
// check for json array
|
||||
if commandValue[0] == '[' {
|
||||
if err := json.Unmarshal([]byte(commandValue), &command); err != nil {
|
||||
command = []string{commandValue}
|
||||
}
|
||||
} else {
|
||||
command = append(command, commandValue)
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
type ResourceHookSelector struct {
|
||||
Namespaces *collections.IncludesExcludes
|
||||
Resources *collections.IncludesExcludes
|
||||
LabelSelector labels.Selector
|
||||
}
|
||||
|
||||
// ResourceHook is a hook for a given resource.
|
||||
type ResourceHook struct {
|
||||
Name string
|
||||
Selector ResourceHookSelector
|
||||
Pre []velerov1api.BackupResourceHook
|
||||
Post []velerov1api.BackupResourceHook
|
||||
}
|
||||
|
||||
func (r ResourceHookSelector) applicableTo(groupResource schema.GroupResource, namespace string, labels labels.Set) bool {
|
||||
if r.Namespaces != nil && !r.Namespaces.ShouldInclude(namespace) {
|
||||
return false
|
||||
}
|
||||
if r.Resources != nil && !r.Resources.ShouldInclude(groupResource.String()) {
|
||||
return false
|
||||
}
|
||||
if r.LabelSelector != nil && !r.LabelSelector.Matches(labels) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ResourceRestoreHook is a restore hook for a given resource.
|
||||
type ResourceRestoreHook struct {
|
||||
Name string
|
||||
Selector ResourceHookSelector
|
||||
RestoreHooks []velerov1api.RestoreResourceHook
|
||||
}
|
||||
|
||||
func getInitRestoreHookFromAnnotation(podName string, annotations map[string]string, log logrus.FieldLogger) *velerov1api.InitRestoreHook {
|
||||
containerImage := annotations[podRestoreHookInitContainerImageAnnotationKey]
|
||||
containerName := annotations[podRestoreHookInitContainerNameAnnotationKey]
|
||||
command := annotations[podRestoreHookInitContainerCommandAnnotationKey]
|
||||
if containerImage == "" {
|
||||
log.Infof("Pod %s has no %s annotation, no initRestoreHook in annotation", podName, podRestoreHookInitContainerImageAnnotationKey)
|
||||
return nil
|
||||
}
|
||||
if command == "" {
|
||||
log.Infof("RestoreHook init contianer for pod %s is using container's default entrypoint", podName, containerImage)
|
||||
}
|
||||
if containerName == "" {
|
||||
uid, err := uuid.NewV4()
|
||||
uuidStr := "deadfeed"
|
||||
if err != nil {
|
||||
log.Errorf("Failed to generate UUID for container name")
|
||||
} else {
|
||||
uuidStr = strings.Split(uid.String(), "-")[0]
|
||||
}
|
||||
containerName = fmt.Sprintf("velero-restore-init-%s", uuidStr)
|
||||
log.Infof("Pod %s has no %s annotation, using generated name %s for initContainer", podName, podRestoreHookInitContainerNameAnnotationKey, containerName)
|
||||
}
|
||||
|
||||
return &velerov1api.InitRestoreHook{
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Image: containerImage,
|
||||
Name: containerName,
|
||||
Command: parseStringToCommand(command),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetRestoreHooksFromSpec returns a list of ResourceRestoreHooks from the restore Spec.
|
||||
func GetRestoreHooksFromSpec(hooksSpec *velerov1api.RestoreHooks) ([]ResourceRestoreHook, error) {
|
||||
if hooksSpec == nil {
|
||||
return []ResourceRestoreHook{}, nil
|
||||
}
|
||||
restoreHooks := make([]ResourceRestoreHook, 0, len(hooksSpec.Resources))
|
||||
for _, rs := range hooksSpec.Resources {
|
||||
rh := ResourceRestoreHook{
|
||||
Name: rs.Name,
|
||||
Selector: ResourceHookSelector{
|
||||
Namespaces: collections.NewIncludesExcludes().Includes(rs.IncludedNamespaces...).Excludes(rs.ExcludedNamespaces...),
|
||||
// these hooks ara applicable only to pods.
|
||||
// TODO: resolve the pods resource via discovery?
|
||||
Resources: collections.NewIncludesExcludes().Includes(kuberesource.Pods.Resource),
|
||||
},
|
||||
// TODO does this work for ExecRestoreHook as well?
|
||||
RestoreHooks: rs.PostHooks,
|
||||
}
|
||||
|
||||
if rs.LabelSelector != nil {
|
||||
ls, err := metav1.LabelSelectorAsSelector(rs.LabelSelector)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
rh.Selector.LabelSelector = ls
|
||||
}
|
||||
restoreHooks = append(restoreHooks, rh)
|
||||
}
|
||||
|
||||
return restoreHooks, nil
|
||||
}
|
||||
|
||||
// getPodExecRestoreHookFromAnnotations returns an ExecRestoreHook based on restore annotations, as
|
||||
// long as the 'command' annotation is present. If it is absent, this returns nil.
|
||||
func getPodExecRestoreHookFromAnnotations(annotations map[string]string, log logrus.FieldLogger) *velerov1api.ExecRestoreHook {
|
||||
commandValue := annotations[podRestoreHookCommandAnnotationKey]
|
||||
if commandValue == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
container := annotations[podRestoreHookContainerAnnotationKey]
|
||||
|
||||
onError := velerov1api.HookErrorMode(annotations[podRestoreHookOnErrorAnnotationKey])
|
||||
if onError != velerov1api.HookErrorModeContinue && onError != velerov1api.HookErrorModeFail {
|
||||
onError = ""
|
||||
}
|
||||
|
||||
var execTimeout time.Duration
|
||||
execTimeoutString := annotations[podRestoreHookTimeoutAnnotationKey]
|
||||
if execTimeoutString != "" {
|
||||
if temp, err := time.ParseDuration(execTimeoutString); err == nil {
|
||||
execTimeout = temp
|
||||
} else {
|
||||
log.Warn(errors.Wrapf(err, "Unable to parse exec timeout %s, ignoring", execTimeoutString))
|
||||
}
|
||||
}
|
||||
|
||||
var waitTimeout time.Duration
|
||||
waitTimeoutString := annotations[podRestoreHookWaitTimeoutAnnotationKey]
|
||||
if waitTimeoutString != "" {
|
||||
if temp, err := time.ParseDuration(waitTimeoutString); err == nil {
|
||||
waitTimeout = temp
|
||||
} else {
|
||||
log.Warn(errors.Wrapf(err, "Unable to parse wait timeout %s, ignoring", waitTimeoutString))
|
||||
}
|
||||
}
|
||||
|
||||
return &velerov1api.ExecRestoreHook{
|
||||
Container: container,
|
||||
Command: parseStringToCommand(commandValue),
|
||||
OnError: onError,
|
||||
ExecTimeout: metav1.Duration{Duration: execTimeout},
|
||||
WaitTimeout: metav1.Duration{Duration: waitTimeout},
|
||||
}
|
||||
}
|
||||
|
||||
type PodExecRestoreHook struct {
|
||||
HookName string
|
||||
HookSource string
|
||||
Hook velerov1api.ExecRestoreHook
|
||||
executed bool
|
||||
}
|
||||
|
||||
// GroupRestoreExecHooks returns a list of hooks to be executed in a pod grouped by
|
||||
// container name. If an exec hook is defined in annotation that is used, else applicable exec
|
||||
// hooks from the restore resource are accumulated.
|
||||
func GroupRestoreExecHooks(
|
||||
resourceRestoreHooks []ResourceRestoreHook,
|
||||
pod *corev1api.Pod,
|
||||
log logrus.FieldLogger,
|
||||
) (map[string][]PodExecRestoreHook, error) {
|
||||
byContainer := map[string][]PodExecRestoreHook{}
|
||||
|
||||
if pod == nil || len(pod.Spec.Containers) == 0 {
|
||||
return byContainer, nil
|
||||
}
|
||||
metadata, err := meta.Accessor(pod)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
hookFromAnnotation := getPodExecRestoreHookFromAnnotations(metadata.GetAnnotations(), log)
|
||||
if hookFromAnnotation != nil {
|
||||
// default to first container in pod if unset
|
||||
if hookFromAnnotation.Container == "" {
|
||||
hookFromAnnotation.Container = pod.Spec.Containers[0].Name
|
||||
}
|
||||
byContainer[hookFromAnnotation.Container] = []PodExecRestoreHook{
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: "annotation",
|
||||
Hook: *hookFromAnnotation,
|
||||
},
|
||||
}
|
||||
return byContainer, nil
|
||||
}
|
||||
|
||||
// No hook found on pod's annotations so check for applicable hooks from the restore spec
|
||||
labels := metadata.GetLabels()
|
||||
namespace := metadata.GetNamespace()
|
||||
for _, rrh := range resourceRestoreHooks {
|
||||
if !rrh.Selector.applicableTo(kuberesource.Pods, namespace, labels) {
|
||||
continue
|
||||
}
|
||||
for _, rh := range rrh.RestoreHooks {
|
||||
if rh.Exec == nil {
|
||||
continue
|
||||
}
|
||||
named := PodExecRestoreHook{
|
||||
HookName: rrh.Name,
|
||||
Hook: *rh.Exec,
|
||||
HookSource: "backupSpec",
|
||||
}
|
||||
// default to first container in pod if unset, without mutating resource restore hook
|
||||
if named.Hook.Container == "" {
|
||||
named.Hook.Container = pod.Spec.Containers[0].Name
|
||||
}
|
||||
byContainer[named.Hook.Container] = append(byContainer[named.Hook.Container], named)
|
||||
}
|
||||
}
|
||||
|
||||
return byContainer, nil
|
||||
}
|
||||
1858
internal/hook/item_hook_handler_test.go
Normal file
1858
internal/hook/item_hook_handler_test.go
Normal file
File diff suppressed because it is too large
Load Diff
275
internal/hook/wait_exec_hook_handler.go
Normal file
275
internal/hook/wait_exec_hook_handler.go
Normal file
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package hook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/podexec"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
)
|
||||
|
||||
type WaitExecHookHandler interface {
|
||||
HandleHooks(
|
||||
ctx context.Context,
|
||||
log logrus.FieldLogger,
|
||||
pod *v1.Pod,
|
||||
byContainer map[string][]PodExecRestoreHook,
|
||||
) []error
|
||||
}
|
||||
|
||||
type ListWatchFactory interface {
|
||||
NewListWatch(namespace string, selector fields.Selector) cache.ListerWatcher
|
||||
}
|
||||
|
||||
type DefaultListWatchFactory struct {
|
||||
PodsGetter cache.Getter
|
||||
}
|
||||
|
||||
func (d *DefaultListWatchFactory) NewListWatch(namespace string, selector fields.Selector) cache.ListerWatcher {
|
||||
return cache.NewListWatchFromClient(d.PodsGetter, "pods", namespace, selector)
|
||||
}
|
||||
|
||||
var _ ListWatchFactory = &DefaultListWatchFactory{}
|
||||
|
||||
type DefaultWaitExecHookHandler struct {
|
||||
ListWatchFactory ListWatchFactory
|
||||
PodCommandExecutor podexec.PodCommandExecutor
|
||||
}
|
||||
|
||||
var _ WaitExecHookHandler = &DefaultWaitExecHookHandler{}
|
||||
|
||||
func (e *DefaultWaitExecHookHandler) HandleHooks(
|
||||
ctx context.Context,
|
||||
log logrus.FieldLogger,
|
||||
pod *v1.Pod,
|
||||
byContainer map[string][]PodExecRestoreHook,
|
||||
) []error {
|
||||
if pod == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If hooks are defined for a container that does not exist in the pod log a warning and discard
|
||||
// those hooks to avoid waiting for a container that will never become ready. After that if
|
||||
// there are no hooks left to be executed return immediately.
|
||||
for containerName := range byContainer {
|
||||
if !podHasContainer(pod, containerName) {
|
||||
log.Warningf("Pod %s does not have container %s: discarding post-restore exec hooks", kube.NamespaceAndName(pod), containerName)
|
||||
delete(byContainer, containerName)
|
||||
}
|
||||
}
|
||||
if len(byContainer) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Every hook in every container can have its own wait timeout. Rather than setting up separate
|
||||
// contexts for each, find the largest wait timeout for any hook that should be executed in
|
||||
// the pod and watch the pod for up to that long. Before executing any hook in a container,
|
||||
// check if that hook has a timeout and skip execution if expired.
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
maxWait := maxHookWait(byContainer)
|
||||
// If no hook has a wait timeout then this function will continue waiting for containers to
|
||||
// become ready until the shared hook context is canceled.
|
||||
if maxWait > 0 {
|
||||
ctx, cancel = context.WithTimeout(ctx, maxWait)
|
||||
}
|
||||
waitStart := time.Now()
|
||||
|
||||
var errors []error
|
||||
|
||||
// The first time this handler is called after a container starts running it will execute all
|
||||
// pending hooks for that container. Subsequent invocations of this handler will never execute
|
||||
// hooks in that container. It uses the byContainer map to keep track of which containers have
|
||||
// not yet been observed to be running. It relies on the Informer not to be called concurrently.
|
||||
// When a container is observed running and its hooks are executed, the container is deleted
|
||||
// from the byContainer map. When the map is empty the watch is ended.
|
||||
handler := func(newObj interface{}) {
|
||||
newPod, ok := newObj.(*v1.Pod)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
podLog := log.WithFields(
|
||||
logrus.Fields{
|
||||
"pod": kube.NamespaceAndName(newPod),
|
||||
},
|
||||
)
|
||||
|
||||
if newPod.Status.Phase == v1.PodSucceeded || newPod.Status.Phase == v1.PodFailed {
|
||||
err := fmt.Errorf("Pod entered phase %s before some post-restore exec hooks ran", newPod.Status.Phase)
|
||||
podLog.Warning(err)
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
|
||||
for containerName, hooks := range byContainer {
|
||||
if !isContainerRunning(newPod, containerName) {
|
||||
podLog.Infof("Container %s is not running: post-restore hooks will not yet be executed", containerName)
|
||||
continue
|
||||
}
|
||||
podMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(newPod)
|
||||
if err != nil {
|
||||
podLog.WithError(err).Error("error unstructuring pod")
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
|
||||
// Sequentially run all hooks for the ready container. The container's hooks are not
|
||||
// removed from the byContainer map until all have completed so that if one fails
|
||||
// remaining unexecuted hooks can be handled by the outer function.
|
||||
for i, hook := range hooks {
|
||||
// This indicates to the outer function not to handle this hook as unexecuted in
|
||||
// case of terminating before deleting this container's slice of hooks from the
|
||||
// byContainer map.
|
||||
byContainer[containerName][i].executed = true
|
||||
|
||||
hookLog := podLog.WithFields(
|
||||
logrus.Fields{
|
||||
"hookSource": hook.HookSource,
|
||||
"hookType": "exec",
|
||||
"hookPhase": "post",
|
||||
},
|
||||
)
|
||||
// Check the individual hook's wait timeout is not expired
|
||||
if hook.Hook.WaitTimeout.Duration != 0 && time.Since(waitStart) > hook.Hook.WaitTimeout.Duration {
|
||||
err := fmt.Errorf("Hook %s in container %s expired before executing", hook.HookName, hook.Hook.Container)
|
||||
hookLog.Error(err)
|
||||
if hook.Hook.OnError == velerov1api.HookErrorModeFail {
|
||||
errors = append(errors, err)
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
}
|
||||
eh := &velerov1api.ExecHook{
|
||||
Container: hook.Hook.Container,
|
||||
Command: hook.Hook.Command,
|
||||
OnError: hook.Hook.OnError,
|
||||
Timeout: hook.Hook.ExecTimeout,
|
||||
}
|
||||
if err := e.PodCommandExecutor.ExecutePodCommand(hookLog, podMap, pod.Namespace, pod.Name, hook.HookName, eh); err != nil {
|
||||
hookLog.WithError(err).Error("Error executing hook")
|
||||
if hook.Hook.OnError == velerov1api.HookErrorModeFail {
|
||||
errors = append(errors, err)
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
delete(byContainer, containerName)
|
||||
}
|
||||
if len(byContainer) == 0 {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
selector := fields.OneTermEqualSelector("metadata.name", pod.Name)
|
||||
lw := e.ListWatchFactory.NewListWatch(pod.Namespace, selector)
|
||||
|
||||
_, podWatcher := cache.NewInformer(lw, pod, 0, cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: handler,
|
||||
UpdateFunc: func(_, newObj interface{}) {
|
||||
handler(newObj)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
err := fmt.Errorf("Pod %s deleted before all hooks were executed", kube.NamespaceAndName(pod))
|
||||
log.Error(err)
|
||||
cancel()
|
||||
},
|
||||
})
|
||||
|
||||
podWatcher.Run(ctx.Done())
|
||||
|
||||
// There are some cases where this function could return with unexecuted hooks: the pod may
|
||||
// be deleted, a hook with OnError mode Fail could fail, or it may timeout waiting for
|
||||
// containers to become ready.
|
||||
// Each unexecuted hook is logged as an error but only hooks with OnError mode Fail return
|
||||
// an error from this function.
|
||||
for _, hooks := range byContainer {
|
||||
for _, hook := range hooks {
|
||||
if hook.executed {
|
||||
continue
|
||||
}
|
||||
err := fmt.Errorf("Hook %s in container %s in pod %s not executed: %v", hook.HookName, hook.Hook.Container, kube.NamespaceAndName(pod), ctx.Err())
|
||||
hookLog := log.WithFields(
|
||||
logrus.Fields{
|
||||
"hookSource": hook.HookSource,
|
||||
"hookType": "exec",
|
||||
"hookPhase": "post",
|
||||
},
|
||||
)
|
||||
hookLog.Error(err)
|
||||
if hook.Hook.OnError == velerov1api.HookErrorModeFail {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func podHasContainer(pod *v1.Pod, containerName string) bool {
|
||||
if pod == nil {
|
||||
return false
|
||||
}
|
||||
for _, c := range pod.Spec.Containers {
|
||||
if c.Name == containerName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isContainerRunning(pod *v1.Pod, containerName string) bool {
|
||||
if pod == nil {
|
||||
return false
|
||||
}
|
||||
for _, cs := range pod.Status.ContainerStatuses {
|
||||
if cs.Name != containerName {
|
||||
continue
|
||||
}
|
||||
return cs.State.Running != nil
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// maxHookWait returns 0 to mean wait indefinitely. Any hook without a wait timeout will cause this
|
||||
// function to return 0.
|
||||
func maxHookWait(byContainer map[string][]PodExecRestoreHook) time.Duration {
|
||||
var maxWait time.Duration
|
||||
for _, hooks := range byContainer {
|
||||
for _, hook := range hooks {
|
||||
if hook.Hook.WaitTimeout.Duration <= 0 {
|
||||
return 0
|
||||
}
|
||||
if hook.Hook.WaitTimeout.Duration > maxWait {
|
||||
maxWait = hook.Hook.WaitTimeout.Duration
|
||||
}
|
||||
}
|
||||
}
|
||||
return maxWait
|
||||
}
|
||||
949
internal/hook/wait_exec_hook_handler_test.go
Normal file
949
internal/hook/wait_exec_hook_handler_test.go
Normal file
@@ -0,0 +1,949 @@
|
||||
/*
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package hook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
fcache "k8s.io/client-go/tools/cache/testing"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
)
|
||||
|
||||
type fakeListWatchFactory struct {
|
||||
lw cache.ListerWatcher
|
||||
}
|
||||
|
||||
func (f *fakeListWatchFactory) NewListWatch(ns string, selector fields.Selector) cache.ListerWatcher {
|
||||
return f.lw
|
||||
}
|
||||
|
||||
var _ ListWatchFactory = &fakeListWatchFactory{}
|
||||
|
||||
func TestWaitExecHandleHooks(t *testing.T) {
|
||||
type change struct {
|
||||
// delta to wait since last change applied or pod added
|
||||
wait time.Duration
|
||||
updated *v1.Pod
|
||||
}
|
||||
type expectedExecution struct {
|
||||
hook *velerov1api.ExecHook
|
||||
name string
|
||||
error error
|
||||
pod *v1.Pod
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
// Used as argument to HandleHooks and first state added to ListerWatcher
|
||||
initialPod *v1.Pod
|
||||
groupResource string
|
||||
byContainer map[string][]PodExecRestoreHook
|
||||
expectedExecutions []expectedExecution
|
||||
expectedErrors []error
|
||||
// changes represents the states of the pod over time. It can be used to test a container
|
||||
// becoming ready at some point after it is first observed by the controller.
|
||||
changes []change
|
||||
sharedHooksContextTimeout time.Duration
|
||||
}{
|
||||
{
|
||||
name: "should return no error when hook from annotation executes successfully",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
podRestoreHookContainerAnnotationKey, "container1",
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeContinue),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
)).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
groupResource: "pods",
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: "annotation",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{time.Second},
|
||||
WaitTimeout: metav1.Duration{time.Minute},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedExecutions: []expectedExecution{
|
||||
{
|
||||
name: "<from-annotation>",
|
||||
hook: &velerov1api.ExecHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
Timeout: metav1.Duration{time.Second},
|
||||
},
|
||||
error: nil,
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithResourceVersion("1")).
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
podRestoreHookContainerAnnotationKey, "container1",
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeContinue),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
)).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
},
|
||||
expectedErrors: nil,
|
||||
},
|
||||
{
|
||||
name: "should return an error when hook from annotation fails with on error mode fail",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
podRestoreHookContainerAnnotationKey, "container1",
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeFail),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
)).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
groupResource: "pods",
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: "annotation",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeFail,
|
||||
ExecTimeout: metav1.Duration{time.Second},
|
||||
WaitTimeout: metav1.Duration{time.Minute},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedExecutions: []expectedExecution{
|
||||
{
|
||||
name: "<from-annotation>",
|
||||
hook: &velerov1api.ExecHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeFail,
|
||||
Timeout: metav1.Duration{time.Second},
|
||||
},
|
||||
error: errors.New("pod hook error"),
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithResourceVersion("1")).
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
podRestoreHookContainerAnnotationKey, "container1",
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeFail),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
)).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
},
|
||||
expectedErrors: []error{errors.New("pod hook error")},
|
||||
},
|
||||
{
|
||||
name: "should return no error when hook from annotation fails with on error mode continue",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
podRestoreHookContainerAnnotationKey, "container1",
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeContinue),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
)).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
groupResource: "pods",
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: "annotation",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{time.Second},
|
||||
WaitTimeout: metav1.Duration{time.Minute},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedExecutions: []expectedExecution{
|
||||
{
|
||||
name: "<from-annotation>",
|
||||
hook: &velerov1api.ExecHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
Timeout: metav1.Duration{time.Second},
|
||||
},
|
||||
error: errors.New("pod hook error"),
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithResourceVersion("1")).
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
podRestoreHookContainerAnnotationKey, "container1",
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeContinue),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
)).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
},
|
||||
expectedErrors: nil,
|
||||
},
|
||||
{
|
||||
name: "should return no error when hook from annotation executes after 10ms wait for container to start",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
podRestoreHookContainerAnnotationKey, "container1",
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeContinue),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
)).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
groupResource: "pods",
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "<from-annotation>",
|
||||
HookSource: "annotation",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
ExecTimeout: metav1.Duration{time.Second},
|
||||
WaitTimeout: metav1.Duration{time.Minute},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedExecutions: []expectedExecution{
|
||||
{
|
||||
name: "<from-annotation>",
|
||||
hook: &velerov1api.ExecHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
Timeout: metav1.Duration{time.Second},
|
||||
},
|
||||
error: nil,
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithResourceVersion("2")).
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
podRestoreHookContainerAnnotationKey, "container1",
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeContinue),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
)).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
},
|
||||
expectedErrors: nil,
|
||||
changes: []change{
|
||||
{
|
||||
wait: 10 * time.Millisecond,
|
||||
updated: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations(
|
||||
podRestoreHookCommandAnnotationKey, "/usr/bin/foo",
|
||||
podRestoreHookContainerAnnotationKey, "container1",
|
||||
podRestoreHookOnErrorAnnotationKey, string(velerov1api.HookErrorModeContinue),
|
||||
podRestoreHookTimeoutAnnotationKey, "1s",
|
||||
podRestoreHookWaitTimeoutAnnotationKey, "1m",
|
||||
)).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return no error when hook from spec executes successfully",
|
||||
groupResource: "pods",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
expectedErrors: nil,
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedExecutions: []expectedExecution{
|
||||
{
|
||||
name: "my-hook-1",
|
||||
hook: &velerov1api.ExecHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
},
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithResourceVersion("1")).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return no error when spec hook with wait timeout expires with OnError mode Continue",
|
||||
groupResource: "pods",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
expectedErrors: nil,
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
WaitTimeout: metav1.Duration{time.Millisecond},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedExecutions: []expectedExecution{},
|
||||
},
|
||||
{
|
||||
name: "should return an error when spec hook with wait timeout expires with OnError mode Fail",
|
||||
groupResource: "pods",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
expectedErrors: []error{errors.New("Hook my-hook-1 in container container1 in pod default/my-pod not executed: context deadline exceeded")},
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeFail,
|
||||
WaitTimeout: metav1.Duration{time.Millisecond},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedExecutions: []expectedExecution{},
|
||||
},
|
||||
{
|
||||
name: "should return an error when shared hooks context is canceled before spec hook with OnError mode Fail executes",
|
||||
groupResource: "pods",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
expectedErrors: []error{errors.New("Hook my-hook-1 in container container1 in pod default/my-pod not executed: context deadline exceeded")},
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeFail,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedExecutions: []expectedExecution{},
|
||||
sharedHooksContextTimeout: time.Millisecond,
|
||||
},
|
||||
{
|
||||
name: "should return no error when shared hooks context is canceled before spec hook with OnError mode Continue executes",
|
||||
expectedErrors: nil,
|
||||
groupResource: "pods",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
OnError: velerov1api.HookErrorModeContinue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedExecutions: []expectedExecution{},
|
||||
sharedHooksContextTimeout: time.Millisecond,
|
||||
},
|
||||
{
|
||||
name: "shoudl return no error with 2 spec hooks in 2 different containers, 1st container starts running after 10ms, 2nd container after 20ms, both succeed",
|
||||
groupResource: "pods",
|
||||
initialPod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
Containers(&v1.Container{
|
||||
Name: "container2",
|
||||
}).
|
||||
// initially both are waiting
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container2",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
expectedErrors: nil,
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
"container2": {
|
||||
{
|
||||
HookName: "my-hook-1",
|
||||
HookSource: "backupSpec",
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
Container: "container2",
|
||||
Command: []string{"/usr/bin/bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedExecutions: []expectedExecution{
|
||||
{
|
||||
name: "my-hook-1",
|
||||
hook: &velerov1api.ExecHook{
|
||||
Container: "container1",
|
||||
Command: []string{"/usr/bin/foo"},
|
||||
},
|
||||
error: nil,
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithResourceVersion("2")).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
Containers(&v1.Container{
|
||||
Name: "container2",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
// container 2 is still waiting when the first hook executes in container1
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container2",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
{
|
||||
name: "my-hook-1",
|
||||
hook: &velerov1api.ExecHook{
|
||||
Container: "container2",
|
||||
Command: []string{"/usr/bin/bar"},
|
||||
},
|
||||
error: nil,
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithResourceVersion("3")).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
Containers(&v1.Container{
|
||||
Name: "container2",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container2",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
},
|
||||
changes: []change{
|
||||
// 1st modification: container1 starts running, resourceVersion 2, container2 still waiting
|
||||
{
|
||||
wait: 10 * time.Millisecond,
|
||||
updated: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithResourceVersion("2")).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
Containers(&v1.Container{
|
||||
Name: "container2",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container2",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
// 2nd modification: container2 starts running, resourceVersion 3
|
||||
{
|
||||
wait: 10 * time.Millisecond,
|
||||
updated: builder.ForPod("default", "my-pod").
|
||||
ObjectMeta(builder.WithResourceVersion("3")).
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
Containers(&v1.Container{
|
||||
Name: "container2",
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container2",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
source := fcache.NewFakeControllerSource()
|
||||
go func() {
|
||||
// This is the state of the pod that will be seen by the AddFunc handler.
|
||||
source.Add(test.initialPod)
|
||||
// Changes holds the versions of the pod over time. Each of these states
|
||||
// will be seen by the UpdateFunc handler.
|
||||
for _, change := range test.changes {
|
||||
time.Sleep(change.wait)
|
||||
source.Modify(change.updated)
|
||||
}
|
||||
}()
|
||||
|
||||
podCommandExecutor := &velerotest.MockPodCommandExecutor{}
|
||||
defer podCommandExecutor.AssertExpectations(t)
|
||||
|
||||
h := &DefaultWaitExecHookHandler{
|
||||
PodCommandExecutor: podCommandExecutor,
|
||||
ListWatchFactory: &fakeListWatchFactory{source},
|
||||
}
|
||||
|
||||
for _, e := range test.expectedExecutions {
|
||||
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(e.pod)
|
||||
assert.Nil(t, err)
|
||||
podCommandExecutor.On("ExecutePodCommand", mock.Anything, obj, e.pod.Namespace, e.pod.Name, e.name, e.hook).Return(e.error)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
if test.sharedHooksContextTimeout > 0 {
|
||||
ctx, _ = context.WithTimeout(ctx, test.sharedHooksContextTimeout)
|
||||
}
|
||||
|
||||
errs := h.HandleHooks(ctx, velerotest.NewLogger(), test.initialPod, test.byContainer)
|
||||
|
||||
// for i, ee := range test.expectedErrors {
|
||||
require.Len(t, errs, len(test.expectedErrors))
|
||||
for i, ee := range test.expectedErrors {
|
||||
assert.EqualError(t, errs[i], ee.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodHasContainer(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *v1.Pod
|
||||
container string
|
||||
expect bool
|
||||
}{
|
||||
{
|
||||
name: "has container",
|
||||
expect: true,
|
||||
container: "container1",
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
Name: "container1",
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
{
|
||||
name: "does not have container",
|
||||
expect: false,
|
||||
container: "container1",
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
Containers(&v1.Container{
|
||||
Name: "container2",
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := podHasContainer(test.pod, test.container)
|
||||
assert.Equal(t, actual, test.expect)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsContainerRunning(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *v1.Pod
|
||||
container string
|
||||
expect bool
|
||||
}{
|
||||
{
|
||||
name: "should return true when running",
|
||||
container: "container1",
|
||||
expect: true,
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
{
|
||||
name: "should return false when no state is set",
|
||||
container: "container1",
|
||||
expect: false,
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
{
|
||||
name: "should return false when waiting",
|
||||
container: "container1",
|
||||
expect: false,
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Waiting: &v1.ContainerStateWaiting{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
{
|
||||
name: "should return true when running and first container is terminated",
|
||||
container: "container1",
|
||||
expect: true,
|
||||
pod: builder.ForPod("default", "my-pod").
|
||||
ContainerStatuses(&v1.ContainerStatus{
|
||||
Name: "container0",
|
||||
State: v1.ContainerState{
|
||||
Terminated: &v1.ContainerStateTerminated{},
|
||||
},
|
||||
},
|
||||
&v1.ContainerStatus{
|
||||
Name: "container1",
|
||||
State: v1.ContainerState{
|
||||
Running: &v1.ContainerStateRunning{},
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := isContainerRunning(test.pod, test.container)
|
||||
assert.Equal(t, actual, test.expect)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxHookWait(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
byContainer map[string][]PodExecRestoreHook
|
||||
expect time.Duration
|
||||
}{
|
||||
{
|
||||
name: "should return 0 for nil map",
|
||||
byContainer: nil,
|
||||
expect: 0,
|
||||
},
|
||||
{
|
||||
name: "should return 0 if all hooks are 0 or negative",
|
||||
expect: 0,
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
ExecTimeout: metav1.Duration{time.Second},
|
||||
WaitTimeout: metav1.Duration{0},
|
||||
},
|
||||
},
|
||||
{
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
WaitTimeout: metav1.Duration{-1},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return biggest wait timeout from multiple hooks in multiple containers",
|
||||
expect: time.Hour,
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
WaitTimeout: metav1.Duration{time.Second},
|
||||
},
|
||||
},
|
||||
{
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
WaitTimeout: metav1.Duration{time.Second},
|
||||
},
|
||||
},
|
||||
},
|
||||
"container2": {
|
||||
{
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
WaitTimeout: metav1.Duration{time.Hour},
|
||||
},
|
||||
},
|
||||
{
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
WaitTimeout: metav1.Duration{time.Minute},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return 0 if any hook does not have a wait timeout",
|
||||
expect: 0,
|
||||
byContainer: map[string][]PodExecRestoreHook{
|
||||
"container1": {
|
||||
{
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
ExecTimeout: metav1.Duration{time.Second},
|
||||
WaitTimeout: metav1.Duration{time.Second},
|
||||
},
|
||||
},
|
||||
{
|
||||
Hook: velerov1api.ExecRestoreHook{
|
||||
WaitTimeout: metav1.Duration{0},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := maxHookWait(test.byContainer)
|
||||
assert.Equal(t, actual, test.expect)
|
||||
})
|
||||
}
|
||||
}
|
||||
92
internal/storage/storagelocation.go
Normal file
92
internal/storage/storagelocation.go
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
// DefaultBackupLocationInfo holds server default backup storage location information
|
||||
type DefaultBackupLocationInfo struct {
|
||||
// StorageLocation is the name of the backup storage location designated as the default
|
||||
StorageLocation string
|
||||
// StoreValidationFrequency is the default validation frequency for any backup storage location
|
||||
StoreValidationFrequency time.Duration
|
||||
}
|
||||
|
||||
// IsReadyToValidate calculates if a given backup storage location is ready to be validated.
|
||||
//
|
||||
// Rules:
|
||||
// Users can choose a validation frequency per location. This will overrite the server's default value
|
||||
// To skip/stop validation, set the frequency to zero
|
||||
// This will always return "true" for the first attempt at validating a location, regardless of its validation frequency setting
|
||||
// Otherwise, it returns "ready" only when NOW is equal to or after the next validation time
|
||||
// (next validation time: last validation time + validation frequency)
|
||||
func IsReadyToValidate(bslValidationFrequency *metav1.Duration, lastValidationTime *metav1.Time, defaultLocationInfo DefaultBackupLocationInfo, log logrus.FieldLogger) bool {
|
||||
validationFrequency := defaultLocationInfo.StoreValidationFrequency
|
||||
// If the bsl validation frequency is not specifically set, skip this block and continue, using the server's default
|
||||
if bslValidationFrequency != nil {
|
||||
validationFrequency = bslValidationFrequency.Duration
|
||||
}
|
||||
|
||||
if validationFrequency == 0 {
|
||||
log.Debug("Validation period for this backup location is set to 0, skipping validation")
|
||||
return false
|
||||
}
|
||||
|
||||
if validationFrequency < 0 {
|
||||
log.Debugf("Validation period must be non-negative, changing from %d to %d", validationFrequency, defaultLocationInfo.StoreValidationFrequency)
|
||||
validationFrequency = defaultLocationInfo.StoreValidationFrequency
|
||||
}
|
||||
|
||||
lastValidation := lastValidationTime
|
||||
if lastValidation != nil { // always ready to validate the first time around, so only even do this check if this has happened before
|
||||
nextValidation := lastValidation.Add(validationFrequency) // next validation time: last validation time + validation frequency
|
||||
if time.Now().UTC().Before(nextValidation) { // ready only when NOW is equal to or after the next validation time
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ListBackupStorageLocations verifies if there are any backup storage locations.
|
||||
// For all purposes, if either there is an error while attempting to fetch items or
|
||||
// if there are no items an error would be returned since the functioning of the system
|
||||
// would be haulted.
|
||||
func ListBackupStorageLocations(ctx context.Context, kbClient client.Client, namespace string) (velerov1api.BackupStorageLocationList, error) {
|
||||
var locations velerov1api.BackupStorageLocationList
|
||||
if err := kbClient.List(ctx, &locations, &client.ListOptions{
|
||||
Namespace: namespace,
|
||||
}); err != nil {
|
||||
return velerov1api.BackupStorageLocationList{}, err
|
||||
}
|
||||
|
||||
if len(locations.Items) == 0 {
|
||||
return velerov1api.BackupStorageLocationList{}, errors.New("no backup storage locations found")
|
||||
}
|
||||
|
||||
return locations, nil
|
||||
}
|
||||
168
internal/storage/storagelocation_test.go
Normal file
168
internal/storage/storagelocation_test.go
Normal file
@@ -0,0 +1,168 @@
|
||||
/*Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
)
|
||||
|
||||
func TestIsReadyToValidate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
bslValidationFrequency *metav1.Duration
|
||||
lastValidationTime *metav1.Time
|
||||
defaultLocationInfo DefaultBackupLocationInfo
|
||||
|
||||
// serverDefaultValidationFrequency time.Duration
|
||||
// backupLocation *velerov1api.BackupStorageLocation
|
||||
ready bool
|
||||
}{
|
||||
{
|
||||
name: "don't validate, since frequency is set to zero",
|
||||
bslValidationFrequency: &metav1.Duration{Duration: 0},
|
||||
defaultLocationInfo: DefaultBackupLocationInfo{
|
||||
StoreValidationFrequency: 0,
|
||||
},
|
||||
ready: false,
|
||||
},
|
||||
{
|
||||
name: "validate as per location setting, as that takes precedence, and always if it has never been validated before regardless of the frequency setting",
|
||||
bslValidationFrequency: &metav1.Duration{Duration: 1 * time.Hour},
|
||||
defaultLocationInfo: DefaultBackupLocationInfo{
|
||||
StoreValidationFrequency: 0,
|
||||
},
|
||||
ready: true,
|
||||
},
|
||||
{
|
||||
name: "don't validate as per location setting, as it is set to zero and that takes precedence",
|
||||
bslValidationFrequency: &metav1.Duration{Duration: 0},
|
||||
defaultLocationInfo: DefaultBackupLocationInfo{
|
||||
StoreValidationFrequency: 1,
|
||||
},
|
||||
ready: false,
|
||||
},
|
||||
{
|
||||
name: "validate as per default setting when location setting is not set",
|
||||
defaultLocationInfo: DefaultBackupLocationInfo{
|
||||
StoreValidationFrequency: 1,
|
||||
},
|
||||
ready: true,
|
||||
},
|
||||
{
|
||||
name: "don't validate when default setting is set to zero and the location setting is not set",
|
||||
defaultLocationInfo: DefaultBackupLocationInfo{
|
||||
StoreValidationFrequency: 0,
|
||||
},
|
||||
ready: false,
|
||||
},
|
||||
{
|
||||
name: "don't validate when now is before the NEXT validation time (validation frequency + last validation time)",
|
||||
bslValidationFrequency: &metav1.Duration{Duration: 1 * time.Second},
|
||||
lastValidationTime: &metav1.Time{Time: time.Now()},
|
||||
defaultLocationInfo: DefaultBackupLocationInfo{
|
||||
StoreValidationFrequency: 0,
|
||||
},
|
||||
ready: false,
|
||||
},
|
||||
{
|
||||
name: "validate when now is equal to the NEXT validation time (validation frequency + last validation time)",
|
||||
bslValidationFrequency: &metav1.Duration{Duration: 1 * time.Second},
|
||||
lastValidationTime: &metav1.Time{Time: time.Now().Add(-1 * time.Second)},
|
||||
defaultLocationInfo: DefaultBackupLocationInfo{
|
||||
StoreValidationFrequency: 0,
|
||||
},
|
||||
ready: true,
|
||||
},
|
||||
{
|
||||
name: "validate when now is after the NEXT validation time (validation frequency + last validation time)",
|
||||
bslValidationFrequency: &metav1.Duration{Duration: 1 * time.Second},
|
||||
lastValidationTime: &metav1.Time{Time: time.Now().Add(-2 * time.Second)},
|
||||
defaultLocationInfo: DefaultBackupLocationInfo{
|
||||
StoreValidationFrequency: 0,
|
||||
},
|
||||
ready: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
log := velerotest.NewLogger()
|
||||
|
||||
g.Expect(IsReadyToValidate(tt.bslValidationFrequency, tt.lastValidationTime, tt.defaultLocationInfo, log)).To(BeIdenticalTo(tt.ready))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListBackupStorageLocations(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
backupLocations *velerov1api.BackupStorageLocationList
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "1 existing location does not return an error",
|
||||
backupLocations: &velerov1api.BackupStorageLocationList{
|
||||
Items: []velerov1api.BackupStorageLocation{
|
||||
*builder.ForBackupStorageLocation("ns-1", "location-1").Result(),
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "multiple existing location does not return an error",
|
||||
backupLocations: &velerov1api.BackupStorageLocationList{
|
||||
Items: []velerov1api.BackupStorageLocation{
|
||||
*builder.ForBackupStorageLocation("ns-1", "location-1").Result(),
|
||||
*builder.ForBackupStorageLocation("ns-1", "location-2").Result(),
|
||||
*builder.ForBackupStorageLocation("ns-1", "location-3").Result(),
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "no existing locations returns an error",
|
||||
backupLocations: &velerov1api.BackupStorageLocationList{},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
client := fake.NewFakeClientWithScheme(scheme.Scheme, tt.backupLocations)
|
||||
if tt.expectError {
|
||||
_, err := ListBackupStorageLocations(context.Background(), client, "ns-1")
|
||||
g.Expect(err).NotTo(BeNil())
|
||||
} else {
|
||||
_, err := ListBackupStorageLocations(context.Background(), client, "ns-1")
|
||||
g.Expect(err).To(BeNil())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
73
internal/velero/serverstatusrequest.go
Normal file
73
internal/velero/serverstatusrequest.go
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package velero
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/buildinfo"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
|
||||
)
|
||||
|
||||
// ServerStatus holds information for retrieving installed
|
||||
// plugins and for updating the ServerStatusRequest timestamp.
|
||||
type ServerStatus struct {
|
||||
PluginRegistry PluginLister
|
||||
Clock clock.Clock
|
||||
}
|
||||
|
||||
// PatchStatusProcessed patches status fields, including loading the plugin info, and updates
|
||||
// the ServerStatusRequest.Status.Phase to ServerStatusRequestPhaseProcessed.
|
||||
func (s *ServerStatus) PatchStatusProcessed(kbClient client.Client, req *velerov1api.ServerStatusRequest, ctx context.Context) error {
|
||||
statusPatch := client.MergeFrom(req.DeepCopyObject())
|
||||
req.Status.ServerVersion = buildinfo.Version
|
||||
req.Status.Phase = velerov1api.ServerStatusRequestPhaseProcessed
|
||||
req.Status.ProcessedTimestamp = &metav1.Time{Time: s.Clock.Now()}
|
||||
req.Status.Plugins = getInstalledPluginInfo(s.PluginRegistry)
|
||||
|
||||
if err := kbClient.Status().Patch(ctx, req, statusPatch); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PluginLister interface {
|
||||
// List returns all PluginIdentifiers for kind.
|
||||
List(kind framework.PluginKind) []framework.PluginIdentifier
|
||||
}
|
||||
|
||||
func getInstalledPluginInfo(pluginLister PluginLister) []velerov1api.PluginInfo {
|
||||
var plugins []velerov1api.PluginInfo
|
||||
for _, v := range framework.AllPluginKinds() {
|
||||
list := pluginLister.List(v)
|
||||
for _, plugin := range list {
|
||||
pluginInfo := velerov1api.PluginInfo{
|
||||
Name: plugin.Name,
|
||||
Kind: plugin.Kind.String(),
|
||||
}
|
||||
plugins = append(plugins, pluginInfo)
|
||||
}
|
||||
}
|
||||
return plugins
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user