mirror of
https://github.com/zitadel/zitadel.git
synced 2025-01-08 09:37:40 +00:00
feat(operator): zitadel and database operator (#1208)
* feat(operator): add base for zitadel operator * fix(operator): changed pipeline to release operator * fix(operator): fmt with only one parameter * fix(operator): corrected workflow job name * fix(zitadelctl): added restore and backuplist command * fix(zitadelctl): scale for restore * chore(container): use scratch for deploy container * fix(zitadelctl): limit image to scratch * fix(migration): added migration scripts for newer version * fix(operator): changed handling of kubeconfig in operator logic * fix(operator): changed handling of secrets in operator logic * fix(operator): use new version of zitadel * fix(operator): added path for migrations * fix(operator): delete doublets of migration scripts * fix(operator): delete subpaths and integrate logic into init container * fix(operator): corrected path in dockerfile for local migrations * fix(operator): added migrations for cockroachdb-secure * fix(operator): delete logic for ambassador module * fix(operator): added read and write secret commands * fix(operator): correct and align operator pipeline with zitadel pipeline * fix(operator): correct yaml error in operator pipeline * fix(operator): correct action name in operator pipeline * fix(operator): correct case-sensitive filename in operator pipeline * fix(operator): upload artifacts from buildx output * fix(operator): corrected attribute spelling error * fix(operator): combined jobs for operator binary and image * fix(operator): added missing comma in operator pipeline * fix(operator): added codecov for operator image * fix(operator): added codecov for operator image * fix(testing): code changes for testing and several unit-tests (#1009) * fix(operator): usage of interface of kubernetes client for testing and several unit-tests * fix(operator): several unit-tests * fix(operator): several unit-tests * fix(operator): changed order for the operator logic * fix(operator): added version of zitadelctl from semantic release * fix(operator): corrected function call with version of zitadelctl * fix(operator): corrected function call with version of zitadelctl * fix(operator): add check output to operator release pipeline * fix(operator): set --short length everywhere to 12 * fix(operator): zitadel setup in job instead of exec with several unit tests * fix(operator): fixes to combine newest zitadel and testing branch * fix(operator): corrected path in Dockerfile * fix(operator): fixed unit-test that was ignored during changes * fix(operator): fixed unit-test that was ignored during changes * fix(operator): corrected Dockerfile to correctly use env variable * fix(operator): quickfix takeoff deployment * fix(operator): corrected the clusterrolename in the applied artifacts * fix: update secure migrations * fix(operator): migrations (#1057) * fix(operator): copied migrations from orbos repository * fix(operator): newest migrations * chore: use cockroach-secure * fix: rename migration * fix: remove insecure cockroach migrations Co-authored-by: Stefan Benz <stefan@caos.ch> * fix: finalize labels * fix(operator): cli logging concurrent and fixe deployment of operator during restore * fix: finalize labels and cli commands * fix: restore * chore: cockroachdb is always secure * chore: use orbos consistent-labels latest commit * test: make tests compatible with new labels * fix: default to sa token for start command * fix: use cockroachdb v12.02 * fix: don't delete flyway user * test: fix migration test * fix: use correct table qualifiers * fix: don't alter sequence ownership * fix: upgrade flyway * fix: change ownership of all dbs and tables to admin user * fix: change defaultdb user * fix: treat clientid status codes >= 400 as errors * fix: reconcile specified ZITADEL version, not binary version * fix: add ca-certs * fix: use latest orbos code * fix: use orbos with fixed race condition * fix: use latest ORBOS code * fix: use latest ORBOS code * fix: make migration and scaling around restoring work * fix(operator): move zitadel operator * chore(migrations): include owner change migration * feat(db): add code base for database operator * fix(db): change used image registry for database operator * fix(db): generated mock * fix(db): add accidentally ignored file * fix(db): add cockroachdb backup image to pipeline * fix(db): correct pipeline and image versions * fix(db): correct version of used orbos * fix(db): correct database import * fix(db): go mod tidy * fix(db): use new version for orbos * fix(migrations): include migrations into zitadelctl binary (#1211) * fix(db): use statik to integrate migrations into binary * fix(migrations): corrections unit tests and pipeline for integrated migrations into zitadelctl binary * fix(migrations): correction in dockerfile for pipeline build * fix(migrations): correction in dockerfile for pipeline build * fix(migrations): dockerfile changes for cache optimization * fix(database): correct used part-of label in database operator * fix(database): correct used selectable label in zitadel operator * fix(operator): correct lables for user secrets in zitadel operator * fix(operator): correct lables for service test in zitadel operator * fix: don't enable database features for user operations (#1227) * fix: don't enable database features for user operations * fix: omit database feature for connection info adapter * fix: use latest orbos version * fix: update ORBOS (#1240) Co-authored-by: Florian Forster <florian@caos.ch> Co-authored-by: Elio Bischof <eliobischof@gmail.com>
This commit is contained in:
parent
3428046ffa
commit
ad25f35539
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@ -17,7 +17,15 @@ updates:
|
||||
prefix: chore
|
||||
include: scope
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/build/"
|
||||
directory: "/build"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 10
|
||||
commit-message:
|
||||
prefix: chore
|
||||
include: scope
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/build/operator"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 10
|
||||
|
21
.github/workflows/codecov.yml
vendored
21
.github/workflows/codecov.yml
vendored
@ -19,6 +19,13 @@ jobs:
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache-op
|
||||
key: ${{ runner.os }}-buildx-op-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-op-
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
@ -32,8 +39,18 @@ jobs:
|
||||
push: false
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
target: go-codecov
|
||||
outputs: type=local,dest=.
|
||||
outputs: type=local,dest=/tmp/zitadel
|
||||
- uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./build/operator/Dockerfile
|
||||
platforms: linux/amd64
|
||||
tags: ${{ env.REGISTRY }}/${{ github.repository }}:coverage
|
||||
push: false
|
||||
cache-from: type=local,src=/tmp/.buildx-cache-op
|
||||
target: go-codecov
|
||||
outputs: type=local,dest=/tmp/operator
|
||||
- uses: codecov/codecov-action@v1
|
||||
with:
|
||||
file: ./profile.cov
|
||||
files: /tmp/zitadel/profile.cov,/tmp/operator/profile.cov
|
||||
name: codecov-go
|
165
.github/workflows/operator.yml
vendored
Normal file
165
.github/workflows/operator.yml
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
name: Operator Release
|
||||
on: push
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.CR_PAT }}
|
||||
REGISTRY: ghcr.io
|
||||
GO_VERSION: '1.15'
|
||||
DOCKER_IMAGE_NAME: ${{ github.repository }}-operator
|
||||
BACKUP_IMAGE_NAME: ${{ github.repository }}-crbackup
|
||||
|
||||
jobs:
|
||||
container:
|
||||
runs-on: ubuntu-18.04
|
||||
name: Build ${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
strategy:
|
||||
matrix:
|
||||
goos: [ 'linux', 'darwin', 'windows' ]
|
||||
goarch: [ 'amd64' ]
|
||||
steps:
|
||||
- name: Source checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set output
|
||||
id: branch
|
||||
run: echo ::set-output name=short_ref::${GITHUB_REF#refs/*/}
|
||||
- name: Semantic Release
|
||||
id: semantic
|
||||
uses: cycjimmy/semantic-release-action@v2
|
||||
with:
|
||||
dry_run: true
|
||||
semantic_version: 17.0.4
|
||||
- name: Set version
|
||||
id: version
|
||||
run: |
|
||||
if ${{ steps.semantic.outputs.new_release_published == 'true' }}; then
|
||||
echo ::set-output name=version::${{ steps.semantic.outputs.new_release_version }}
|
||||
else
|
||||
echo ::set-output name=version::${{ steps.branch.outputs.short_ref }}
|
||||
fi
|
||||
- name: Check outputs
|
||||
run: |
|
||||
echo ${{ steps.branch.outputs.short_ref }}
|
||||
echo ${{ steps.version.outputs.version }}
|
||||
- name: Generate Short SHA Container Tag
|
||||
id: vars
|
||||
run: echo "::set-output name=sha_short::SHA-$(git rev-parse --short=12 HEAD)"
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-op-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-op-
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.CR_PAT }}
|
||||
registry: ${{ env.REGISTRY }}
|
||||
- uses: docker/build-push-action@v2
|
||||
name: onlybuild
|
||||
with:
|
||||
context: .
|
||||
file: ./build/operator/Dockerfile
|
||||
platforms: linux/amd64
|
||||
tags: ${{ env.REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.vars.outputs.sha_short }},${{ env.REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.branch.outputs.short_ref }}
|
||||
push: false
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,mode=max,dest=/tmp/.buildx-cache
|
||||
outputs: type=local,dest=/tmp/operator
|
||||
build-args: |
|
||||
OS=${{ matrix.goos }}
|
||||
ARCH=${{ matrix.goarch }}
|
||||
VERSION=${{ steps.version.outputs.version }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: zitadelctl-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: /tmp/operator/zitadelctl
|
||||
- uses: docker/build-push-action@v2
|
||||
if: ${{ matrix.goos == 'linux' && matrix.goarch == 'amd64' }}
|
||||
name: buildandpush
|
||||
with:
|
||||
context: .
|
||||
file: ./build/operator/Dockerfile
|
||||
platforms: linux/amd64
|
||||
tags: ${{ env.REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.vars.outputs.sha_short }},${{ env.REGISTRY }}/${{ env.DOCKER_IMAGE_NAME }}:${{ steps.branch.outputs.short_ref }}
|
||||
push: true
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,mode=max,dest=/tmp/.buildx-cache
|
||||
build-args: |
|
||||
OS=${{ matrix.goos }}
|
||||
ARCH=${{ matrix.goarch }}
|
||||
VERSION=${{ steps.version.outputs.version }}
|
||||
- uses: docker/build-push-action@v2
|
||||
if: ${{ matrix.goos == 'linux' && matrix.goarch == 'amd64' }}
|
||||
name: buildandpushcrbackup
|
||||
with:
|
||||
context: .
|
||||
file: ./build/cr-backup/Dockerfile
|
||||
platforms: linux/amd64
|
||||
tags: ${{ env.REGISTRY }}/${{ env.BACKUP_IMAGE_NAME }}:${{ steps.vars.outputs.sha_short }},${{ env.REGISTRY }}/${{ env.BACKUP_IMAGE_NAME }}:${{ steps.branch.outputs.short_ref }}
|
||||
push: true
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,mode=max,dest=/tmp/.buildx-cache
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-18.04
|
||||
needs: [ container ]
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ github.actor }}
|
||||
DOCKER_PASSWORD: ${{ secrets.CR_PAT }}
|
||||
steps:
|
||||
- name: Source checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Generate Short SHA Container Tag
|
||||
id: vars
|
||||
run: echo "::set-output name=sha_short::SHA-$(git rev-parse --short=12 HEAD)"
|
||||
- name: Check output
|
||||
run: echo ${{ steps.vars.outputs.sha_short }}
|
||||
- name: Docker Login
|
||||
run: docker login $REGISTRY -u $GITHUB_ACTOR -p $GITHUB_TOKEN
|
||||
- name: Docker Pull short-sha
|
||||
run: docker pull $REGISTRY/$DOCKER_IMAGE_NAME:${{ steps.vars.outputs.sha_short }}
|
||||
- name: Docker Pull short-sha
|
||||
run: docker pull $REGISTRY/$BACKUP_IMAGE_NAME:${{ steps.vars.outputs.sha_short }}
|
||||
- name: Download binaries
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: .artifacts
|
||||
- name: Semantic Release
|
||||
id: semantic
|
||||
uses: cycjimmy/semantic-release-action@v2
|
||||
with:
|
||||
dry_run: false
|
||||
semantic_version: 17.0.4
|
||||
- name: Do something when a new release published
|
||||
if: steps.semantic.outputs.new_release_published == 'true'
|
||||
run: |
|
||||
echo ${{ steps.semantic.outputs.new_release_version }}
|
||||
echo ${{ steps.semantic.outputs.new_release_major_version }}
|
||||
echo ${{ steps.semantic.outputs.new_release_minor_version }}
|
||||
echo ${{ steps.semantic.outputs.new_release_patch_version }}
|
||||
- name: Docker Tag Version
|
||||
run: |
|
||||
docker tag $REGISTRY/$DOCKER_IMAGE_NAME:${{ steps.vars.outputs.sha_short }} $REGISTRY/$DOCKER_IMAGE_NAME:${{ steps.semantic.outputs.new_release_version }}
|
||||
docker tag $REGISTRY/$BACKUP_IMAGE_NAME:${{ steps.vars.outputs.sha_short }} $REGISTRY/$BACKUP_IMAGE_NAME:${{ steps.semantic.outputs.new_release_version }}
|
||||
if: steps.semantic.outputs.new_release_published == 'true'
|
||||
- name: Docker Tag Latest
|
||||
run: |
|
||||
docker tag $REGISTRY/$DOCKER_IMAGE_NAME:${{ steps.vars.outputs.sha_short }} $REGISTRY/$DOCKER_IMAGE_NAME:latest
|
||||
docker tag $REGISTRY/$BACKUP_IMAGE_NAME:${{ steps.vars.outputs.sha_short }} $REGISTRY/$BACKUP_IMAGE_NAME:latest
|
||||
if: steps.semantic.outputs.new_release_published == 'true'
|
||||
- name: Docker Push Version
|
||||
run: |
|
||||
docker push $REGISTRY/$DOCKER_IMAGE_NAME:${{ steps.semantic.outputs.new_release_version }}
|
||||
docker push $REGISTRY/$BACKUP_IMAGE_NAME:${{ steps.semantic.outputs.new_release_version }}
|
||||
if: steps.semantic.outputs.new_release_published == 'true'
|
||||
- name: Docker Push Latest
|
||||
run: |
|
||||
docker push $REGISTRY/$DOCKER_IMAGE_NAME:latest
|
||||
docker push $REGISTRY/$BACKUP_IMAGE_NAME:latest
|
||||
if: steps.semantic.outputs.new_release_published == 'true'
|
@ -1,4 +1,4 @@
|
||||
name: Release
|
||||
name: Zitadel Release
|
||||
on: push
|
||||
|
||||
env:
|
||||
@ -20,7 +20,7 @@ jobs:
|
||||
run: echo ${{ steps.branch.outputs.short_ref }}
|
||||
- name: Generate Short SHA Container Tag
|
||||
id: vars
|
||||
run: echo "::set-output name=sha_short::SHA-$(git rev-parse --short HEAD)"
|
||||
run: echo "::set-output name=sha_short::SHA-$(git rev-parse --short=12 HEAD)"
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
@ -33,7 +33,7 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.CR_PAT }}
|
||||
@ -59,7 +59,7 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
- name: Generate Short SHA Container Tag
|
||||
id: vars
|
||||
run: echo "::set-output name=sha_short::SHA-$(git rev-parse --short HEAD)"
|
||||
run: echo "::set-output name=sha_short::SHA-$(git rev-parse --short=12 HEAD)"
|
||||
- name: Docker Login
|
||||
run: docker login $REGISTRY -u $GITHUB_ACTOR -p $GITHUB_TOKEN
|
||||
- name: Docker Pull short-sha
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -4,6 +4,7 @@
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
zitadelctl
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
@ -17,6 +18,7 @@ coverage.txt
|
||||
#Debug
|
||||
__debug_bin
|
||||
debug
|
||||
sandbox.go
|
||||
|
||||
# IDE
|
||||
.idea
|
||||
@ -37,11 +39,12 @@ cockroach-data/*
|
||||
#binaries
|
||||
cmd/zitadel/zitadel
|
||||
**/statik/statik.go
|
||||
zitadelctl
|
||||
|
||||
# buildfolders and generated js
|
||||
tmp/
|
||||
console/src/app/proto/generated/
|
||||
|
||||
pkg/grpc/*/*.pb.*
|
||||
pkg/grpc/*/*.swagger.json
|
||||
pkg/grpc/*/mock/*.mock.go
|
||||
pkg/grpc/*/*.swagger.json
|
@ -3,6 +3,21 @@ module.exports = {
|
||||
plugins: [
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/github"
|
||||
["@semantic-release/github", {
|
||||
"assets": [
|
||||
{
|
||||
"path": ".artifacts/zitadel-darwin-amd64/zitadelctl",
|
||||
"label": "Zitadelctl Darwin x86_64"
|
||||
},
|
||||
{
|
||||
"path": ".artifacts/zitadel-linux-amd64/zitadelctl",
|
||||
"label": "Zitadelctl Linux x86_64"
|
||||
},
|
||||
{
|
||||
"path": ".artifacts/zitadel-windows-amd64/zitadelctl",
|
||||
"label": "Zitadelctl Windows x86_64"
|
||||
}
|
||||
]
|
||||
}],
|
||||
]
|
||||
};
|
||||
|
22
build/cr-backup/Dockerfile
Normal file
22
build/cr-backup/Dockerfile
Normal file
@ -0,0 +1,22 @@
|
||||
FROM cockroachdb/cockroach:v20.2.3
|
||||
|
||||
RUN microdnf install curl wget tar gzip
|
||||
|
||||
RUN wget https://storage.googleapis.com/oauth2l/latest/linux_amd64.tgz
|
||||
RUN tar zxvf linux_amd64.tgz
|
||||
RUN mv linux_amd64/oauth2l /usr/local/bin/oauth2l && rm -rf linux_amd64
|
||||
|
||||
COPY build/cr-backup/scripts/backup-cockroach.sh /scripts/backup.sh
|
||||
RUN chmod +x /scripts/backup.sh
|
||||
|
||||
COPY build/cr-backup/scripts/restore-cockroach.sh /scripts/restore.sh
|
||||
RUN chmod +x /scripts/restore.sh
|
||||
|
||||
COPY build/cr-backup/scripts/clean-db-cockroach.sh /scripts/clean-db.sh
|
||||
RUN chmod +x /scripts/clean-db.sh
|
||||
COPY build/cr-backup/scripts/clean-migration-cockroach.sh /scripts/clean-migration.sh
|
||||
RUN chmod +x /scripts/clean-migration.sh
|
||||
COPY build/cr-backup/scripts/clean-user-cockroach.sh /scripts/clean-user.sh
|
||||
RUN chmod +x /scripts/clean-user.sh
|
||||
|
||||
ENTRYPOINT [ "/cockroach" ]
|
17
build/cr-backup/scripts/backup-cockroach.sh
Normal file
17
build/cr-backup/scripts/backup-cockroach.sh
Normal file
@ -0,0 +1,17 @@
|
||||
env=$1
|
||||
bucket=$2
|
||||
db=$3
|
||||
folder=$4
|
||||
safile=$5
|
||||
certs=$6
|
||||
name=$7
|
||||
|
||||
filenamelocal=zitadel-${db}.sql
|
||||
filenamebucket=zitadel-${db}-${name}.sql
|
||||
|
||||
/cockroach/cockroach.sh dump --dump-mode=data --certs-dir=${certs} --host=cockroachdb-public:26257 ${db} > ${folder}/${filenamelocal}
|
||||
curl -X POST \
|
||||
-H "$(oauth2l header --json ${safile} cloud-platform)" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data-binary @${folder}/${filenamelocal} \
|
||||
"https://storage.googleapis.com/upload/storage/v1/b/${bucket}/o?uploadType=media&name=${env}/${name}/${filenamebucket}"
|
4
build/cr-backup/scripts/clean-db-cockroach.sh
Normal file
4
build/cr-backup/scripts/clean-db-cockroach.sh
Normal file
@ -0,0 +1,4 @@
|
||||
certs=$1
|
||||
db=$2
|
||||
|
||||
/cockroach/cockroach.sh sql --certs-dir=${certs} --host=cockroachdb-public:26257 -e "DROP DATABASE IF EXISTS ${db} CASCADE;"
|
3
build/cr-backup/scripts/clean-migration-cockroach.sh
Normal file
3
build/cr-backup/scripts/clean-migration-cockroach.sh
Normal file
@ -0,0 +1,3 @@
|
||||
certs=$1
|
||||
|
||||
/cockroach/cockroach.sh sql --certs-dir=${certs} --host=cockroachdb-public:26257 -e "TRUNCATE defaultdb.flyway_schema_history;"
|
4
build/cr-backup/scripts/clean-user-cockroach.sh
Normal file
4
build/cr-backup/scripts/clean-user-cockroach.sh
Normal file
@ -0,0 +1,4 @@
|
||||
certs=$1
|
||||
db=$2
|
||||
|
||||
/cockroach/cockroach.sh sql --certs-dir=${certs} --host=cockroachdb-public:26257 -e "DROP USER IF EXISTS ${db};"
|
33
build/cr-backup/scripts/restore-cockroach.sh
Normal file
33
build/cr-backup/scripts/restore-cockroach.sh
Normal file
@ -0,0 +1,33 @@
|
||||
bucket=$1
|
||||
env=$2
|
||||
name=$3
|
||||
db=$4
|
||||
safile=$5
|
||||
certs=$6
|
||||
|
||||
urlencode() {
|
||||
# urlencode <string>
|
||||
old_lc_collate=$LC_COLLATE
|
||||
LC_COLLATE=C
|
||||
|
||||
local length="${#1}"
|
||||
for (( i = 0; i < length; i++ )); do
|
||||
local c="${1:i:1}"
|
||||
case $c in
|
||||
[a-zA-Z0-9.~_-]) printf "$c" ;;
|
||||
*) printf '%%%02X' "'$c" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
LC_COLLATE=$old_lc_collate
|
||||
}
|
||||
|
||||
filenamelocal=zitadel-${db}.sql
|
||||
filenamebucket=zitadel-${db}-${name}.sql
|
||||
|
||||
curl -X GET \
|
||||
-H "$(oauth2l header --json ${safile} cloud-platform)" \
|
||||
-o "${filenamelocal}" \
|
||||
"https://storage.googleapis.com/storage/v1/b/${bucket}/o/$(urlencode ${env}/${name}/${filenamebucket})?alt=media"
|
||||
|
||||
/cockroach/cockroach.sh sql --certs-dir=${certs} --host=cockroachdb-public:26257 --database=${db} < ${filenamelocal}
|
@ -4,7 +4,7 @@ services:
|
||||
angular:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: build/dockerfile
|
||||
dockerfile: dockerfile
|
||||
target: dev-angular-build
|
||||
args:
|
||||
ENV: dev
|
||||
@ -14,7 +14,7 @@ services:
|
||||
go:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: build/dockerfile
|
||||
dockerfile: dockerfile
|
||||
target: dev-go-build
|
||||
args:
|
||||
ENV: dev
|
||||
|
@ -4,7 +4,7 @@ services:
|
||||
angular:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: build/dockerfile
|
||||
dockerfile: dockerfile
|
||||
target: dev-angular-build
|
||||
args:
|
||||
ENV: dev
|
||||
@ -14,7 +14,7 @@ services:
|
||||
go:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: build/dockerfile
|
||||
dockerfile: dockerfile
|
||||
target: dev-go-build
|
||||
args:
|
||||
ENV: dev
|
||||
|
@ -89,7 +89,11 @@ COPY --from=go-base /go/src/github.com/caos/zitadel/pkg/ .
|
||||
## Go test
|
||||
FROM go-base as go-test
|
||||
COPY . .
|
||||
RUN go test -race -v -coverprofile=profile.cov ./...
|
||||
#Migrations for cockroach-secure
|
||||
RUN go install github.com/rakyll/statik
|
||||
RUN ./build/operator/prebuild.sh ./migrations
|
||||
|
||||
RUN go test -race -v -coverprofile=profile.cov $(go list ./... | grep -v /operator/)
|
||||
|
||||
## Go test
|
||||
FROM scratch as go-codecov
|
||||
@ -114,16 +118,16 @@ RUN go get github.com/go-delve/delve/cmd/dlv
|
||||
#######################
|
||||
FROM alpine:latest as artifact
|
||||
RUN adduser -D zitadel
|
||||
COPY cmd/zitadel/*.yaml /
|
||||
COPY --from=prod-go-build /go/src/github.com/caos/zitadel/zitadel-linux-amd64 /zitadel
|
||||
RUN chmod a+x zitadel
|
||||
COPY cmd/zitadel/*.yaml /app/
|
||||
COPY --from=prod-go-build /go/src/github.com/caos/zitadel/zitadel-linux-amd64 /app/zitadel
|
||||
RUN chmod a+x /app/zitadel
|
||||
RUN ls -la /
|
||||
|
||||
## Scratch Image
|
||||
FROM scratch as final
|
||||
COPY --from=artifact /etc/passwd /etc/passwd
|
||||
## TODO copy should be removed once the operator branch is merged
|
||||
COPY --from=artifact / /
|
||||
COPY --from=artifact /etc/ssl/certs /etc/ssl/certs
|
||||
COPY --from=artifact /app /
|
||||
USER zitadel
|
||||
HEALTHCHECK NONE
|
||||
ENTRYPOINT ["/zitadel"]
|
||||
ENTRYPOINT ["/zitadel"]
|
||||
|
1
build/operator/.dockerignore
Normal file
1
build/operator/.dockerignore
Normal file
@ -0,0 +1 @@
|
||||
**/statik/statik.go
|
59
build/operator/Dockerfile
Normal file
59
build/operator/Dockerfile
Normal file
@ -0,0 +1,59 @@
|
||||
#######################
|
||||
## By default we build the prod enviroment
|
||||
ARG ENV=prod
|
||||
|
||||
#######################
|
||||
## Go base build
|
||||
## Speed up this step by mounting your local go mod pkg directory
|
||||
#######################
|
||||
FROM golang:1.15 as go-base
|
||||
|
||||
WORKDIR src/github.com/caos/zitadel/
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
|
||||
## Go test
|
||||
FROM go-base as go-test
|
||||
COPY . .
|
||||
#Migrations for cockroach-secure
|
||||
RUN go install github.com/rakyll/statik
|
||||
RUN ./build/operator/prebuild.sh ./migrations
|
||||
|
||||
RUN go test -race -v -coverprofile=profile.cov ./operator/...
|
||||
|
||||
## Go test
|
||||
FROM scratch as go-codecov
|
||||
COPY --from=go-test /go/src/github.com/caos/zitadel/profile.cov profile.cov
|
||||
|
||||
## Go prod build
|
||||
FROM go-test as prod-go-build
|
||||
|
||||
|
||||
ARG ARCH=amd64
|
||||
ARG OS=linux
|
||||
ARG VERSION=none
|
||||
RUN CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH} go build -a -installsuffix cgo -ldflags "-extldflags '-static' -X main.Version=${VERSION}" -o zitadelctl cmd/zitadelctl/main.go
|
||||
|
||||
## Go dev build
|
||||
FROM go-base as dev-go-build
|
||||
RUN go get github.com/go-delve/delve/cmd/dlv
|
||||
|
||||
#######################
|
||||
## Final Production Image
|
||||
#######################
|
||||
FROM alpine:latest as artifact
|
||||
RUN adduser -D zitadel
|
||||
|
||||
ARG ARCH=amd64
|
||||
ARG OS=linux
|
||||
COPY --from=prod-go-build /go/src/github.com/caos/zitadel/zitadelctl /app/zitadelctl
|
||||
RUN chmod a+x /app/zitadelctl
|
||||
|
||||
## Scratch Image
|
||||
FROM scratch as final
|
||||
COPY --from=artifact /etc/passwd /etc/passwd
|
||||
COPY --from=artifact /app /
|
||||
USER zitadel
|
||||
HEALTHCHECK NONE
|
||||
ENTRYPOINT ["/zitadelctl"]
|
5
build/operator/prebuild.sh
Executable file
5
build/operator/prebuild.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
statik -src=$1
|
3
changelog.config.js
Normal file
3
changelog.config.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
"disableEmoji": true
|
||||
};
|
50
cmd/database-debug/main.go
Normal file
50
cmd/database-debug/main.go
Normal file
@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
|
||||
"github.com/caos/zitadel/operator/start"
|
||||
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/zitadel/operator/helpers"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
orbconfig := flag.String("orbconfig", "~/.orb/config", "The orbconfig file to use")
|
||||
kubeconfig := flag.String("kubeconfig", "~/.kube/config", "The kubeconfig file to use")
|
||||
verbose := flag.Bool("verbose", false, "Print debug levelled logs")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
monitor := mntr.Monitor{
|
||||
OnInfo: mntr.LogMessage,
|
||||
OnChange: mntr.LogMessage,
|
||||
OnError: mntr.LogError,
|
||||
}
|
||||
|
||||
if *verbose {
|
||||
monitor = monitor.Verbose()
|
||||
}
|
||||
|
||||
kc, err := ioutil.ReadFile(helpers.PruneHome(*kubeconfig))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := start.Database(
|
||||
monitor,
|
||||
helpers.PruneHome(*orbconfig),
|
||||
kubernetes.NewK8sClient(monitor, strPtr(string(kc))),
|
||||
strPtr("database-development"),
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func strPtr(str string) *string {
|
||||
return &str
|
||||
}
|
47
cmd/operator-debug/main.go
Normal file
47
cmd/operator-debug/main.go
Normal file
@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/zitadel/operator/helpers"
|
||||
"github.com/caos/zitadel/operator/start"
|
||||
)
|
||||
|
||||
func main() {
|
||||
orbconfig := flag.String("orbconfig", "~/.orb/config", "The orbconfig file to use")
|
||||
kubeconfig := flag.String("kubeconfig", "~/.kube/config", "The kubeconfig file to use")
|
||||
verbose := flag.Bool("verbose", false, "Print debug levelled logs")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
monitor := mntr.Monitor{
|
||||
OnInfo: mntr.LogMessage,
|
||||
OnChange: mntr.LogMessage,
|
||||
OnError: mntr.LogError,
|
||||
}
|
||||
|
||||
if *verbose {
|
||||
monitor = monitor.Verbose()
|
||||
}
|
||||
|
||||
kc, err := ioutil.ReadFile(helpers.PruneHome(*kubeconfig))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := start.Operator(
|
||||
monitor,
|
||||
helpers.PruneHome(*orbconfig),
|
||||
kubernetes.NewK8sClient(monitor, strPtr(string(kc))),
|
||||
strPtr("local-debugging"),
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func strPtr(str string) *string {
|
||||
return &str
|
||||
}
|
73
cmd/zitadelctl/cmds/backup.go
Normal file
73
cmd/zitadelctl/cmds/backup.go
Normal file
@ -0,0 +1,73 @@
|
||||
package cmds
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/zitadel/operator/api"
|
||||
"github.com/caos/zitadel/operator/start"
|
||||
"github.com/spf13/cobra"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func BackupCommand(rv RootValues) *cobra.Command {
|
||||
var (
|
||||
kubeconfig string
|
||||
backup string
|
||||
cmd = &cobra.Command{
|
||||
Use: "backup",
|
||||
Short: "Instant backup",
|
||||
Long: "Instant backup",
|
||||
}
|
||||
)
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&kubeconfig, "kubeconfig", "~/.kube/config", "Kubeconfig of cluster where the backup should be done")
|
||||
flags.StringVar(&backup, "backup", "", "Name used for backup folder")
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) (err error) {
|
||||
_, monitor, orbConfig, gitClient, version, errFunc, err := rv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errFunc(err)
|
||||
}()
|
||||
|
||||
if err := gitClient.Configure(orbConfig.URL, []byte(orbConfig.Repokey)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gitClient.Clone(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
found, err := api.ExistsDatabaseYml(gitClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
|
||||
value, err := ioutil.ReadFile(kubeconfig)
|
||||
if err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
kubeconfigStr := string(value)
|
||||
|
||||
k8sClient := kubernetes.NewK8sClient(monitor, &kubeconfigStr)
|
||||
if k8sClient.Available() {
|
||||
if err := start.Backup(
|
||||
monitor,
|
||||
orbConfig.Path,
|
||||
k8sClient,
|
||||
backup,
|
||||
&version,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return cmd
|
||||
}
|
54
cmd/zitadelctl/cmds/backuplist.go
Normal file
54
cmd/zitadelctl/cmds/backuplist.go
Normal file
@ -0,0 +1,54 @@
|
||||
package cmds
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/caos/zitadel/pkg/databases"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func BackupListCommand(rv RootValues) *cobra.Command {
|
||||
var (
|
||||
cmd = &cobra.Command{
|
||||
Use: "backuplist",
|
||||
Short: "Get a list of all backups",
|
||||
Long: "Get a list of all backups",
|
||||
}
|
||||
)
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
_, monitor, orbConfig, gitClient, _, errFunc, err := rv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errFunc(err)
|
||||
}()
|
||||
|
||||
if err := gitClient.Configure(orbConfig.URL, []byte(orbConfig.Repokey)); err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := gitClient.Clone(); err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
backups, err := databases.ListBackups(monitor, gitClient)
|
||||
if err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
sort.Slice(backups, func(i, j int) bool {
|
||||
return backups[i] > backups[j]
|
||||
})
|
||||
for _, backup := range backups {
|
||||
fmt.Println(backup)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return cmd
|
||||
}
|
57
cmd/zitadelctl/cmds/readsecret.go
Normal file
57
cmd/zitadelctl/cmds/readsecret.go
Normal file
@ -0,0 +1,57 @@
|
||||
package cmds
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/caos/orbos/pkg/secret"
|
||||
"github.com/caos/zitadel/operator/secrets"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func ReadSecretCommand(rv RootValues) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "readsecret [path]",
|
||||
Short: "Print a secrets decrypted value to stdout",
|
||||
Long: "Print a secrets decrypted value to stdout.\nIf no path is provided, a secret can interactively be chosen from a list of all possible secrets",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Example: `zitadelctl readsecret zitadel.emailappkey > ~/emailappkey`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
_, monitor, orbConfig, gitClient, _, errFunc, err := rv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errFunc(err)
|
||||
}()
|
||||
if err := gitClient.Configure(orbConfig.URL, []byte(orbConfig.Repokey)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gitClient.Clone(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path := ""
|
||||
if len(args) > 0 {
|
||||
path = args[0]
|
||||
}
|
||||
|
||||
value, err := secret.Read(
|
||||
monitor,
|
||||
gitClient,
|
||||
path,
|
||||
secrets.GetAllSecretsFunc(orbConfig))
|
||||
if err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := os.Stdout.Write([]byte(value)); err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
100
cmd/zitadelctl/cmds/restore.go
Normal file
100
cmd/zitadelctl/cmds/restore.go
Normal file
@ -0,0 +1,100 @@
|
||||
package cmds
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/caos/zitadel/operator/helpers"
|
||||
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/zitadel/operator/start"
|
||||
"github.com/caos/zitadel/pkg/databases"
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func RestoreCommand(rv RootValues) *cobra.Command {
|
||||
var (
|
||||
backup string
|
||||
kubeconfig string
|
||||
cmd = &cobra.Command{
|
||||
Use: "restore",
|
||||
Short: "Restore from backup",
|
||||
Long: "Restore from backup",
|
||||
}
|
||||
)
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&backup, "backup", "", "Backup used for db restore")
|
||||
flags.StringVar(&kubeconfig, "kubeconfig", "~/.kube/config", "Kubeconfig for ZITADEL operator deployment")
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
_, monitor, orbConfig, gitClient, version, errFunc, err := rv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errFunc(err)
|
||||
}()
|
||||
|
||||
kubeconfig = helpers.PruneHome(kubeconfig)
|
||||
|
||||
if err := gitClient.Configure(orbConfig.URL, []byte(orbConfig.Repokey)); err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := gitClient.Clone(); err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := ioutil.ReadFile(kubeconfig)
|
||||
if err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
kubeconfigStr := string(value)
|
||||
|
||||
k8sClient := kubernetes.NewK8sClient(monitor, &kubeconfigStr)
|
||||
if k8sClient.Available() {
|
||||
list, err := databases.ListBackups(monitor, gitClient)
|
||||
if err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if backup == "" {
|
||||
prompt := promptui.Select{
|
||||
Label: "Select backup to restore",
|
||||
Items: list,
|
||||
}
|
||||
|
||||
_, result, err := prompt.Run()
|
||||
if err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
backup = result
|
||||
}
|
||||
existing := false
|
||||
for _, listedBackup := range list {
|
||||
if listedBackup == backup {
|
||||
existing = true
|
||||
}
|
||||
}
|
||||
|
||||
if !existing {
|
||||
monitor.Error(errors.New("chosen backup is not existing"))
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := start.Restore(monitor, gitClient, orbConfig, k8sClient, backup, &version); err != nil {
|
||||
monitor.Error(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return cmd
|
||||
}
|
72
cmd/zitadelctl/cmds/root.go
Normal file
72
cmd/zitadelctl/cmds/root.go
Normal file
@ -0,0 +1,72 @@
|
||||
package cmds
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/git"
|
||||
"github.com/caos/orbos/pkg/orb"
|
||||
"github.com/caos/zitadel/operator/helpers"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type RootValues func() (context.Context, mntr.Monitor, *orb.Orb, *git.Client, string, errFunc, error)
|
||||
|
||||
type errFunc func(err error) error
|
||||
|
||||
func RootCommand(version string) (*cobra.Command, RootValues) {
|
||||
|
||||
var (
|
||||
verbose bool
|
||||
orbConfigPath string
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "zitadelctl [flags]",
|
||||
Short: "Interact with your IAM orbs",
|
||||
Long: `zitadelctl launches zitadel and simplifies common tasks such as updating your kubeconfig.
|
||||
Participate in our community on https://github.com/caos/orbos
|
||||
and visit our website at https://caos.ch`,
|
||||
Example: `$ mkdir -p ~/.orb
|
||||
$ cat > ~/.orb/myorb << EOF
|
||||
> url: git@github.com:me/my-orb.git
|
||||
> masterkey: "$(gopass my-secrets/orbs/myorb/masterkey)"
|
||||
> repokey: |
|
||||
> $(cat ~/.ssh/myorbrepo | sed s/^/\ \ /g)
|
||||
> EOF
|
||||
$ orbctl -f ~/.orb/myorb [command]
|
||||
`,
|
||||
}
|
||||
|
||||
flags := cmd.PersistentFlags()
|
||||
flags.StringVarP(&orbConfigPath, "orbconfig", "f", "~/.orb/config", "Path to the file containing the orbs git repo URL, deploy key and the master key for encrypting and decrypting secrets")
|
||||
flags.BoolVar(&verbose, "verbose", false, "Print debug levelled logs")
|
||||
|
||||
return cmd, func() (context.Context, mntr.Monitor, *orb.Orb, *git.Client, string, errFunc, error) {
|
||||
|
||||
monitor := mntr.Monitor{
|
||||
OnInfo: mntr.LogMessage,
|
||||
OnChange: mntr.LogMessage,
|
||||
OnError: mntr.LogError,
|
||||
}
|
||||
|
||||
if verbose {
|
||||
monitor = monitor.Verbose()
|
||||
}
|
||||
|
||||
prunedPath := helpers.PruneHome(orbConfigPath)
|
||||
orbConfig, err := orb.ParseOrbConfig(prunedPath)
|
||||
if err != nil {
|
||||
orbConfig = &orb.Orb{Path: prunedPath}
|
||||
return nil, mntr.Monitor{}, nil, nil, "", nil, err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
return ctx, monitor, orbConfig, git.New(ctx, monitor, "orbos", "orbos@caos.ch"), version, func(err error) error {
|
||||
if err != nil {
|
||||
monitor.Error(err)
|
||||
}
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
}
|
82
cmd/zitadelctl/cmds/start.go
Normal file
82
cmd/zitadelctl/cmds/start.go
Normal file
@ -0,0 +1,82 @@
|
||||
package cmds
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/zitadel/operator/helpers"
|
||||
"github.com/caos/zitadel/operator/start"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func StartOperator(rv RootValues) *cobra.Command {
|
||||
var (
|
||||
kubeconfig string
|
||||
cmd = &cobra.Command{
|
||||
Use: "operator",
|
||||
Short: "Launch a ZITADEL operator",
|
||||
Long: "Ensures a desired state of ZITADEL",
|
||||
}
|
||||
)
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&kubeconfig, "kubeconfig", "", "Kubeconfig for ZITADEL operator deployment")
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
_, monitor, orbConfig, _, version, errFunc, err := rv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errFunc(err)
|
||||
}()
|
||||
|
||||
kubeconfig = helpers.PruneHome(kubeconfig)
|
||||
|
||||
k8sClient, err := kubernetes.NewK8sClientWithPath(monitor, kubeconfig)
|
||||
if err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if k8sClient.Available() {
|
||||
if err := start.Operator(monitor, orbConfig.Path, k8sClient, &version); err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func StartDatabase(rv RootValues) *cobra.Command {
|
||||
var (
|
||||
kubeconfig string
|
||||
cmd = &cobra.Command{
|
||||
Use: "database",
|
||||
Short: "Launch a database operator",
|
||||
Long: "Ensures a desired state of the database",
|
||||
}
|
||||
)
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&kubeconfig, "kubeconfig", "", "kubeconfig used by zitadel operator")
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) (err error) {
|
||||
_, monitor, orbConfig, _, version, errFunc, err := rv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errFunc(err)
|
||||
}()
|
||||
|
||||
k8sClient, err := kubernetes.NewK8sClientWithPath(monitor, kubeconfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if k8sClient.Available() {
|
||||
return start.Database(monitor, orbConfig.Path, k8sClient, &version)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return cmd
|
||||
}
|
127
cmd/zitadelctl/cmds/takeoff.go
Normal file
127
cmd/zitadelctl/cmds/takeoff.go
Normal file
@ -0,0 +1,127 @@
|
||||
package cmds
|
||||
|
||||
import (
|
||||
orbdb "github.com/caos/zitadel/operator/database/kinds/orb"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/caos/zitadel/operator/helpers"
|
||||
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/git"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/zitadel/operator/api"
|
||||
"github.com/caos/zitadel/operator/zitadel/kinds/orb"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func TakeoffCommand(rv RootValues) *cobra.Command {
|
||||
var (
|
||||
kubeconfig string
|
||||
cmd = &cobra.Command{
|
||||
Use: "takeoff",
|
||||
Short: "Launch a ZITADEL operator on the orb",
|
||||
Long: "Ensures a desired state of the resources on the orb",
|
||||
}
|
||||
)
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&kubeconfig, "kubeconfig", "~/.kube/config", "Kubeconfig for ZITADEL operator deployment")
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
_, monitor, orbConfig, gitClient, _, errFunc, err := rv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errFunc(err)
|
||||
}()
|
||||
kubeconfig = helpers.PruneHome(kubeconfig)
|
||||
|
||||
if err := gitClient.Configure(orbConfig.URL, []byte(orbConfig.Repokey)); err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := gitClient.Clone(); err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := ioutil.ReadFile(kubeconfig)
|
||||
if err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
kubeconfigStr := string(value)
|
||||
|
||||
if err := deployOperator(
|
||||
monitor,
|
||||
gitClient,
|
||||
&kubeconfigStr,
|
||||
); err != nil {
|
||||
monitor.Error(err)
|
||||
}
|
||||
|
||||
if err := deployDatabase(
|
||||
monitor,
|
||||
gitClient,
|
||||
&kubeconfigStr,
|
||||
); err != nil {
|
||||
monitor.Error(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func deployOperator(monitor mntr.Monitor, gitClient *git.Client, kubeconfig *string) error {
|
||||
found, err := api.ExistsZitadelYml(gitClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
monitor.Info("No ZITADEL operator deployed as no zitadel.yml present")
|
||||
return nil
|
||||
}
|
||||
|
||||
if found {
|
||||
k8sClient := kubernetes.NewK8sClient(monitor, kubeconfig)
|
||||
|
||||
if k8sClient.Available() {
|
||||
desiredTree, err := api.ReadZitadelYml(gitClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := orb.Reconcile(monitor, desiredTree, true)(k8sClient); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deployDatabase(monitor mntr.Monitor, gitClient *git.Client, kubeconfig *string) error {
|
||||
found, err := api.ExistsDatabaseYml(gitClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
k8sClient := kubernetes.NewK8sClient(monitor, kubeconfig)
|
||||
|
||||
if k8sClient.Available() {
|
||||
tree, err := api.ReadDatabaseYml(gitClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := orbdb.Reconcile(
|
||||
monitor,
|
||||
tree)(k8sClient); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
monitor.Info("Failed to connect to k8s")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
115
cmd/zitadelctl/cmds/writesecret.go
Normal file
115
cmd/zitadelctl/cmds/writesecret.go
Normal file
@ -0,0 +1,115 @@
|
||||
package cmds
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/caos/orbos/pkg/secret"
|
||||
"github.com/caos/zitadel/operator/secrets"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func WriteSecretCommand(rv RootValues) *cobra.Command {
|
||||
|
||||
var (
|
||||
value string
|
||||
file string
|
||||
stdin bool
|
||||
cmd = &cobra.Command{
|
||||
Use: "writesecret [path]",
|
||||
Short: "Encrypt a secret and push it to the repository",
|
||||
Long: "Encrypt a secret and push it to the repository.\nIf no path is provided, a secret can interactively be chosen from a list of all possible secrets",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Example: `orbctl writesecret mystaticprovider.bootstrapkey --file ~/.ssh/my-orb-bootstrap
|
||||
orbctl writesecret mygceprovider.google_application_credentials_value --value "$(cat $GOOGLE_APPLICATION_CREDENTIALS)" `,
|
||||
}
|
||||
)
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&value, "value", "", "Secret value to encrypt")
|
||||
flags.StringVarP(&file, "file", "s", "", "File containing the value to encrypt")
|
||||
flags.BoolVar(&stdin, "stdin", false, "Value to encrypt is read from standard input")
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
_, monitor, orbConfig, gitClient, _, errFunc, err := rv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = errFunc(err)
|
||||
}()
|
||||
|
||||
s, err := key(value, file, stdin)
|
||||
if err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := gitClient.Configure(orbConfig.URL, []byte(orbConfig.Repokey)); err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := gitClient.Clone(); err != nil {
|
||||
monitor.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
path := ""
|
||||
if len(args) > 0 {
|
||||
path = args[0]
|
||||
}
|
||||
|
||||
if err := secret.Write(
|
||||
monitor,
|
||||
gitClient,
|
||||
path,
|
||||
s,
|
||||
secrets.GetAllSecretsFunc(orbConfig),
|
||||
secrets.PushFunc(),
|
||||
); err != nil {
|
||||
monitor.Error(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func key(value string, file string, stdin bool) (string, error) {
|
||||
|
||||
channels := 0
|
||||
if value != "" {
|
||||
channels++
|
||||
}
|
||||
if file != "" {
|
||||
channels++
|
||||
}
|
||||
if stdin {
|
||||
channels++
|
||||
}
|
||||
|
||||
if channels != 1 {
|
||||
return "", errors.New("Key must be provided eighter by value or by file path or by standard input")
|
||||
}
|
||||
|
||||
if value != "" {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
readFunc := func() ([]byte, error) {
|
||||
return ioutil.ReadFile(file)
|
||||
}
|
||||
if stdin {
|
||||
readFunc = func() ([]byte, error) {
|
||||
return ioutil.ReadAll(os.Stdin)
|
||||
}
|
||||
}
|
||||
|
||||
key, err := readFunc()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(key), err
|
||||
}
|
31
cmd/zitadelctl/main.go
Normal file
31
cmd/zitadelctl/main.go
Normal file
@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/caos/zitadel/cmd/zitadelctl/cmds"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
Version = "unknown"
|
||||
)
|
||||
|
||||
func main() {
|
||||
rootCmd, rootValues := cmds.RootCommand(Version)
|
||||
rootCmd.Version = fmt.Sprintf("%s\n", Version)
|
||||
|
||||
rootCmd.AddCommand(
|
||||
cmds.StartOperator(rootValues),
|
||||
cmds.TakeoffCommand(rootValues),
|
||||
cmds.BackupListCommand(rootValues),
|
||||
cmds.RestoreCommand(rootValues),
|
||||
cmds.ReadSecretCommand(rootValues),
|
||||
cmds.WriteSecretCommand(rootValues),
|
||||
cmds.BackupCommand(rootValues),
|
||||
cmds.StartDatabase(rootValues),
|
||||
)
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
18
go.mod
18
go.mod
@ -4,6 +4,7 @@ go 1.15
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.71.0 // indirect
|
||||
cloud.google.com/go/storage v1.10.0
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v0.13.0
|
||||
@ -16,9 +17,9 @@ require (
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc
|
||||
github.com/caos/logging v0.0.2
|
||||
github.com/caos/oidc v0.13.2
|
||||
github.com/caos/orbos v1.5.14-0.20210205131708-6dc812182dc0
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.1.0
|
||||
github.com/duo-labs/webauthn v0.0.0-20200714211715-1daaee874e43
|
||||
github.com/envoyproxy/protoc-gen-validate v0.4.1
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
|
||||
github.com/golang/mock v1.4.4
|
||||
@ -30,7 +31,6 @@ require (
|
||||
github.com/gorilla/schema v1.2.0
|
||||
github.com/gorilla/securecookie v1.1.1
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/imdario/mergo v0.3.11 // indirect
|
||||
@ -41,16 +41,19 @@ require (
|
||||
github.com/kevinburke/rest v0.0.0-20200429221318-0d2892b400f8 // indirect
|
||||
github.com/kevinburke/twilio-go v0.0.0-20200810163702-320748330fac
|
||||
github.com/lib/pq v1.9.0
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/manifoldco/promptui v0.7.0
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect; indirect github.com/mitchellh/copystructure v1.0.0 // indirect
|
||||
github.com/mitchellh/copystructure v1.0.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.1 // indirect
|
||||
github.com/nicksnyder/go-i18n/v2 v2.1.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/otp v1.2.0
|
||||
github.com/prometheus/client_golang v1.8.0 // indirect
|
||||
github.com/prometheus/common v0.15.0 // indirect
|
||||
github.com/rakyll/statik v0.1.7
|
||||
github.com/rs/cors v1.7.0
|
||||
github.com/sony/sonyflake v1.0.0
|
||||
github.com/spf13/cobra v0.0.7
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect
|
||||
github.com/ttacon/libphonenumber v1.1.0
|
||||
@ -66,10 +69,15 @@ require (
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
|
||||
golang.org/x/text v0.3.4
|
||||
golang.org/x/tools v0.0.0-20201103235415-b653051172e4
|
||||
google.golang.org/api v0.34.0
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20201103154000-415bd0cd5df6
|
||||
google.golang.org/genproto v0.0.0-20201103154000-415bd0cd5df6 // indirect
|
||||
google.golang.org/grpc v1.34.0
|
||||
google.golang.org/protobuf v1.25.0
|
||||
gopkg.in/square/go-jose.v2 v2.5.1
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c
|
||||
gotest.tools v2.2.0+incompatible
|
||||
k8s.io/api v0.18.5
|
||||
k8s.io/apiextensions-apiserver v0.18.5
|
||||
k8s.io/apimachinery v0.18.5
|
||||
)
|
||||
|
419
go.sum
419
go.sum
@ -44,6 +44,18 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/AlecAivazis/survey/v2 v2.0.8 h1:zVjWKN+JIAfmrq6nGWG3DfLS8ypEBhxYy0p7FM+riFk=
|
||||
github.com/AlecAivazis/survey/v2 v2.0.8/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk=
|
||||
github.com/AppsFlyer/go-sundheit v0.2.0 h1:FArqX+HbqZ6U32RC3giEAWRUpkggqxHj91KIvxNgwjU=
|
||||
github.com/AppsFlyer/go-sundheit v0.2.0/go.mod h1:rCRkVTMQo7/krF7xQ9X0XEF1an68viFR6/Gy02q+4ds=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
@ -58,22 +70,34 @@ github.com/DataDog/sketches-go v0.0.1/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGt
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v0.13.0 h1:fjKUtfldCPIF4nIzAAj3LzP8Lrd3DuRIMiFdOsj4fLc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v0.13.0/go.mod h1:q/paYxLXKVhwfC3lzLfhtL54fAx14wzMN9DundQOBMc=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
|
||||
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
|
||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
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/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/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb h1:EVl3FJLQCzSbgBezKo/1A4ADnJ4mtJZ0RvnNzDJ44nY=
|
||||
github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
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=
|
||||
@ -82,92 +106,128 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=
|
||||
github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.31.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/benbjohnson/clock v1.0.3 h1:vkLuvpK4fmtSCuo60+yC63p7y0BmQ8gm5ZXGuBCJyXg=
|
||||
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
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/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/caos/logging v0.0.2 h1:ebg5C/HN0ludYR+WkvnFjwSExF4wvyiWPyWGcKMYsoo=
|
||||
github.com/caos/logging v0.0.0-20191210002624-b3260f690a6a/go.mod h1:9LKiDE2ChuGv6CHYif/kiugrfEXu9AwDiFWSreX7Wp0=
|
||||
github.com/caos/logging v0.0.2/go.mod h1:9LKiDE2ChuGv6CHYif/kiugrfEXu9AwDiFWSreX7Wp0=
|
||||
github.com/caos/oidc v0.13.2 h1:52oP3KB1UrZuwraBTLuwM9ItRIhJQMYOm1J5uQ0sYXw=
|
||||
github.com/caos/oidc v0.6.2/go.mod h1:ozoi3b+aY33gzdvjz4w90VZShIHGsmDa0goruuV0arQ=
|
||||
github.com/caos/oidc v0.13.2/go.mod h1:dLvfYUiAt9ORfl77L/KkcWuR/N0ll8Ry1nD2ERsamDY=
|
||||
github.com/caos/orbos v1.5.14-0.20210128140136-842933949472 h1:iti4tAKxBknjJkQcDKWaxlj9Jbng5kz5TpQzzyda49o=
|
||||
github.com/caos/orbos v1.5.14-0.20210128140136-842933949472/go.mod h1:ZLxNgPuYIlSvr80trezGGUIXng9gY2hHEdky/m0B/P0=
|
||||
github.com/caos/orbos v1.5.14-0.20210202122121-ad32524ffc73 h1:usYmCT11HvwxBCk1+DSCmEU6CVYhzY8jHaQHSJMrxlg=
|
||||
github.com/caos/orbos v1.5.14-0.20210202122121-ad32524ffc73/go.mod h1:ZLxNgPuYIlSvr80trezGGUIXng9gY2hHEdky/m0B/P0=
|
||||
github.com/caos/orbos v1.5.14-0.20210205131708-6dc812182dc0 h1:N+KYBwuQO3QPr/nTUaNwjAetjp3NU4MP8Nv9Iue53UE=
|
||||
github.com/caos/orbos v1.5.14-0.20210205131708-6dc812182dc0/go.mod h1:ZLxNgPuYIlSvr80trezGGUIXng9gY2hHEdky/m0B/P0=
|
||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
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/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7 h1:Puu1hUwfps3+1CUzYdAZXijuvLuRMirgiXdf3zsM2Ig=
|
||||
github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/cloudflare/cloudflare-go v0.12.1/go.mod h1:gmzHQPAyHh8N8UgX0Z+3rSMRbNj47JDEbzXDICHVXys=
|
||||
github.com/cloudscale-ch/cloudscale-go-sdk v1.6.0/go.mod h1:FhOTOCgKAVvRRMQc1mC0D7xK/3zYnmcZBWFXNkacvMc=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.1.0 h1:zicZlBhWZu6wfK7Ezg4Owdc3HamLpRdBllPTT9tb+2k=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.1.0/go.mod h1:ilhrLnPDDwGHL+iK2UxQhp1UzUhst8sfItSAgCYwAyg=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
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/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-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/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/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-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
|
||||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
||||
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
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/duo-labs/webauthn v0.0.0-20200714211715-1daaee874e43 h1:eEEfwrmEwl0LVuWz/VkAefdgtPbX174Huu5dxxceihI=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
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-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/duo-labs/webauthn v0.0.0-20200714211715-1daaee874e43/go.mod h1:/X2OJiJxjQ7alqWZqX9EtBTmZc+4qQ0LvZ1k5wP67RM=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.4.1 h1:7dLaJvASGRD7X49jSCSXXHwKPm0ZN9r9kJD+p+vS7dM=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.4.1/go.mod h1:E+IEazqdaWv3FrnGtZIu3b9fPFMK8AzeTTrk9SfVwWs=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fxamacker/cbor/v2 v2.2.0 h1:6eXqdDDe588rSYAi1HfZKbx6YYQO4mxQ9eC6xYpU/JQ=
|
||||
github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@ -177,10 +237,52 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO
|
||||
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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
|
||||
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
|
||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
|
||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
|
||||
github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
|
||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
||||
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
|
||||
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
|
||||
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
@ -189,11 +291,10 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
@ -206,6 +307,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@ -223,11 +325,12 @@ github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=
|
||||
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/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
|
||||
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
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=
|
||||
@ -257,36 +360,39 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleinterns/cloud-operations-api-mock v0.0.0-20200709193332-a1e58c29bdd3 h1:eHv/jVY/JNop1xg2J9cBb4EzyMpWZoNCP1BslSAIkOI=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.1.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/googleinterns/cloud-operations-api-mock v0.0.0-20200709193332-a1e58c29bdd3/go.mod h1:h/KNeRx7oYU4SpA4SoY7W2/NxDKEEVuwA6j9A27L4OI=
|
||||
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/csrf v1.7.0 h1:mMPjV5/3Zd460xCavIkppUdvnl5fPXMpv2uz2Zyg7/Y=
|
||||
github.com/gorilla/csrf v1.7.0/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA=
|
||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
|
||||
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
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/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
|
||||
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/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1/go.mod h1:oVMjMN64nzEcepv1kdZKgx1qNYt4Ro0Gqefiq2JWdis=
|
||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@ -303,20 +409,21 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 h1:KUDFlmBg2buRWNzIcwLlKvfcnujcHQRQ1As1LoaCLAM=
|
||||
github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
@ -365,33 +472,38 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
|
||||
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
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=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kevinburke/go-types v0.0.0-20200309064045-f2d4aea18a7a h1:Z7+SSApKiwPjNic+NF9+j7h657Uyvdp/jA3iTKhpj4E=
|
||||
github.com/kataras/tablewriter v0.0.0-20180708051242-e063d29b7c23/go.mod h1:kBSna6b0/RzsOcOZf515vAXwSsXYusl2U7SA0XP09yI=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kevinburke/go-types v0.0.0-20200309064045-f2d4aea18a7a/go.mod h1:/Pk5i/SqYdYv1cie5wGwoZ4P6TpgMi+Yf58mtJSHdOw=
|
||||
github.com/kevinburke/go.uuid v1.2.0 h1:+1qP8NdkJfgOSTrrrUuA7h0djr1VY77HFXYjR+zUcUo=
|
||||
github.com/kevinburke/go.uuid v1.2.0/go.mod h1:9gVngk1Hq1FjwewVAjsWEUT+xc6jP+p62CASaGmQ0NQ=
|
||||
github.com/kevinburke/rest v0.0.0-20200429221318-0d2892b400f8 h1:KpuDJTaTPQAyWqETt70dHX3pMz65/XYTAZymrKKNvh8=
|
||||
github.com/kevinburke/rest v0.0.0-20200429221318-0d2892b400f8/go.mod h1:pD+iEcdAGVXld5foVN4e24zb/6fnb60tgZPZ3P/3T/I=
|
||||
github.com/kevinburke/twilio-go v0.0.0-20200810163702-320748330fac h1:qQ7NAZEHpTyDfmZBH79KEiH3OK47Z+KvYVSR4sS3650=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kevinburke/twilio-go v0.0.0-20200810163702-320748330fac/go.mod h1:Fm9alkN1/LPVY1eqD/psyMwPWE4VWl4P01/nTYZKzBk=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
@ -399,28 +511,39 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/landoop/tableprinter v0.0.0-20200805134727-ea32388e35c1/go.mod h1:f0X1c0za3TbET/rl5ThtCSel0+G3/yZ8iuU9BxnyVK0=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.4.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
|
||||
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/lyft/protoc-gen-star v0.5.1/go.mod h1:9toiA3cC7z5uVbODF7kEQ91Xn7XNFkVUl+SrEe+ZORU=
|
||||
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
|
||||
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||
github.com/magiconair/properties v1.8.0/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=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/manifoldco/promptui v0.7.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
@ -430,35 +553,43 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
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-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/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/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
|
||||
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
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=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
||||
@ -466,17 +597,22 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
|
||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.1.1 h1:ATCOanRDlrfKVB4WHAdJnLEqZtDmKYsweqsOUYflnBU=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.1.1/go.mod h1:d++QJC9ZVf7pa48qrsRWhMJ5pSHIPmS3OLqK1niyLxs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
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/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
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=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
@ -488,65 +624,74 @@ github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pires/go-proxyproto v0.3.0 h1:++BY4zWOpWha50IDjdnp7+NRTLuOHqMQC5PkgS7I4u4=
|
||||
github.com/pires/go-proxyproto v0.3.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
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.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||
github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw=
|
||||
github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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.1.0/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.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
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.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM=
|
||||
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/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/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
|
||||
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
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/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v0.0.0-20200419222939-1884f454f8ea/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
@ -555,71 +700,88 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
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/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=
|
||||
github.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=
|
||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/afero v1.3.4/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
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=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
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.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||
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=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 h1:5u+EJUQiosu3JFX0XS0qTf5FznsMOzTjGqavBGuCbo0=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2/go.mod h1:4kyMkleCiLkgY6z8gK5BkI01ChBtxR0ro3I1ZDcGM3w=
|
||||
github.com/ttacon/libphonenumber v1.1.0 h1:tC6kE4t8UI4OqQVQjW5q8gSWhG2wnY5moEpSEORdYm4=
|
||||
github.com/ttacon/libphonenumber v1.1.0/go.mod h1:E0TpmdVMq5dyVlQ7oenAkhsLu86OkUl+yR4OAxyEg/M=
|
||||
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/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
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=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
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=
|
||||
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opentelemetry.io/contrib v0.13.0 h1:q34CFu5REx9Dt2ksESHC/doIjFJkEg1oV3aSwlL5JR0=
|
||||
go.opentelemetry.io/contrib v0.13.0/go.mod h1:HzCu6ebm0ywgNxGaEfs3izyJOMP4rZnzxycyTgpI5Sg=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.13.0 h1:Ys1lnE8Y6rv3aKc9Ha13n7UM4pMHC0kvLSFtNx+gUfY=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.13.0/go.mod h1:ffigAFAlfY9AfFwJocEw88qbbvjAKfvqZg5tLyZv0l0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.13.0 h1:dnZy1afzxEDrHybTYoJE1bQ3fphNwZF2ipSsynlITP4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.13.0/go.mod h1:SeQm4RTCcZ2/hlMSTuHb7nwIROe5odBtgfKx+7MMqEs=
|
||||
go.opentelemetry.io/otel v0.13.0 h1:2isEnyzjjJZq6r2EKMsFj4TxiQiexsM04AVhwbR/oBA=
|
||||
go.opentelemetry.io/otel v0.13.0/go.mod h1:dlSNewoRYikTkotEnxdmuBHgzT+k/idJSfDv/FxEnOY=
|
||||
go.opentelemetry.io/otel/exporters/metric/prometheus v0.13.0 h1:Jf7AdsEoHKtNTWxXLj/g9XGjsGpdk0otEf0lx00r2Ps=
|
||||
go.opentelemetry.io/otel/exporters/metric/prometheus v0.13.0/go.mod h1:Tyh3ACxU9a1tu1mF4at7xvNu+BaiPThrr5XZmsoIW7g=
|
||||
go.opentelemetry.io/otel/exporters/otlp v0.13.0 h1:iithmYmMAfLFgCW5TcRXHpXR5NTWO7nGtX3WcBiusVE=
|
||||
go.opentelemetry.io/otel/exporters/otlp v0.13.0/go.mod h1:YHH58UrGcqCKtBkY7sl3zPKpxBzfC1HUUYMRQONJJ9E=
|
||||
go.opentelemetry.io/otel/exporters/stdout v0.13.0 h1:A+XiGIPQbGoJoBOJfKAKnZyiUSjSWvL3XWETUvtom5k=
|
||||
go.opentelemetry.io/otel/exporters/stdout v0.13.0/go.mod h1:JJt8RpNY6K+ft9ir3iKpceCvT/rhzJXEExGrWFCbv1o=
|
||||
go.opentelemetry.io/otel/sdk v0.13.0 h1:4VCfpKamZ8GtnepXxMRurSpHpMKkcxhtO33z1S4rGDQ=
|
||||
go.opentelemetry.io/otel/sdk v0.13.0/go.mod h1:dKvLH8Uu8LcEPlSAUsfW7kMGaJBhk/1NYvpPZ6wIMbU=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
@ -634,16 +796,25 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||
@ -678,12 +849,13 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -692,16 +864,21 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/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=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@ -725,7 +902,9 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTi
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
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-20190517181255-950ef44c6e07/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191122200657-5d9234df094c/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc=
|
||||
@ -738,8 +917,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -748,20 +927,27 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191206220618-eeba5f6aabab/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -790,6 +976,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -801,11 +988,15 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
|
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
@ -816,11 +1007,15 @@ golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBn
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@ -846,7 +1041,6 @@ golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWc
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200701151220-7cb253f4c4f8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
@ -855,14 +1049,12 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201030143252-cf7a54d06671/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201103235415-b653051172e4 h1:Qe0EMgvVYb6tmJhJHljCj3gS96hvSTkGNaIzp/ivq10=
|
||||
golang.org/x/tools v0.0.0-20201103235415-b653051172e4/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
@ -878,6 +1070,7 @@ google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.26.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
@ -890,7 +1083,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/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=
|
||||
@ -928,7 +1120,6 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201103154000-415bd0cd5df6 h1:rMoZiLTOobSD3eg30lPMcFkBFNSyKUQQIQlw/hsAXME=
|
||||
google.golang.org/genproto v0.0.0-20201103154000-415bd0cd5df6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
@ -954,7 +1145,6 @@ google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
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=
|
||||
@ -970,17 +1160,25 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
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=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
|
||||
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@ -989,14 +1187,17 @@ gopkg.in/yaml.v2 v2.2.3/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.7/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-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/postgres v1.0.5/go.mod h1:qrD92UurYzNctBMVCJ8C3VQEjffEuphycXtxOudXNCA=
|
||||
gorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
gorm.io/gorm v1.20.6/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@ -1005,8 +1206,44 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA=
|
||||
k8s.io/api v0.18.5 h1:fKbCxr+U3fu7k6jB+QeYPD/c6xKYeSJ2KVWmyUypuWM=
|
||||
k8s.io/api v0.18.5/go.mod h1:tN+e/2nbdGKOAH55NMV8oGrMG+3uRlA9GaRfvnCCSNk=
|
||||
k8s.io/apiextensions-apiserver v0.18.5 h1:pvbXjB/BRXZiO+/Erp5Pxr+lnhDCv5uxNxHh3FLGZ/g=
|
||||
k8s.io/apiextensions-apiserver v0.18.5/go.mod h1:woZ7PkEIMHjhHIyApvOwkGOkBLUYKuet0VWVkPTQ/Fs=
|
||||
k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
|
||||
k8s.io/apimachinery v0.18.5 h1:Lh6tgsM9FMkC12K5T5QjRm7rDs6aQN5JHkA0JomULDM=
|
||||
k8s.io/apimachinery v0.18.5/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
|
||||
k8s.io/apiserver v0.18.5/go.mod h1:+1XgOMq7YJ3OyqPNSJ54EveHwCoBWcJT9CaPycYI5ps=
|
||||
k8s.io/cli-runtime v0.18.3/go.mod h1:pqbbi4nqRIQhUWAVzen8uE8DD/zcZLwf+8sQYO4lwLk=
|
||||
k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
|
||||
k8s.io/client-go v0.18.5 h1:cLhGZdOmyPhwtt20Lrb7uAqxxB1uvY+NTmNJvno1oKA=
|
||||
k8s.io/client-go v0.18.5/go.mod h1:EsiD+7Fx+bRckKWZXnAXRKKetm1WuzPagH4iOSC8x58=
|
||||
k8s.io/code-generator v0.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
|
||||
k8s.io/code-generator v0.18.5/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
|
||||
k8s.io/component-base v0.18.3/go.mod h1:bp5GzGR0aGkYEfTj+eTY0AN/vXTgkJdQXjNTTVUaa3k=
|
||||
k8s.io/component-base v0.18.5/go.mod h1:RSbcboNk4B+S8Acs2JaBOVW3XNz1+A637s2jL+QQrlU=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
k8s.io/kubectl v0.18.3/go.mod h1:k/EpvXBDgEsHBzWr0A44l9+ArvYi3txBBnzXBjQasUQ=
|
||||
k8s.io/metrics v0.18.3/go.mod h1:TkuJE3ezDZ1ym8pYkZoEzJB7HDiFE7qxl+EmExEBoPA=
|
||||
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU=
|
||||
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
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/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=
|
||||
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
|
||||
|
@ -6,14 +6,14 @@ CREATE DATABASE authz;
|
||||
CREATE DATABASE eventstore;
|
||||
|
||||
|
||||
CREATE USER eventstore;
|
||||
CREATE USER eventstore WITH PASSWORD ${eventstorepassword};
|
||||
GRANT SELECT, INSERT ON DATABASE eventstore TO eventstore;
|
||||
|
||||
CREATE USER management;
|
||||
CREATE USER management WITH PASSWORD ${managementpassword};
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON DATABASE management TO management;
|
||||
GRANT SELECT, INSERT ON DATABASE eventstore TO management;
|
||||
|
||||
CREATE USER adminapi;
|
||||
CREATE USER adminapi WITH PASSWORD ${adminapipassword};
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE, DROP ON DATABASE adminapi TO adminapi;
|
||||
GRANT SELECT, INSERT ON DATABASE eventstore TO adminapi;
|
||||
GRANT SELECT, INSERT, UPDATE, DROP, DELETE ON DATABASE auth TO adminapi;
|
||||
@ -21,15 +21,15 @@ GRANT SELECT, INSERT, UPDATE, DROP, DELETE ON DATABASE authz TO adminapi;
|
||||
GRANT SELECT, INSERT, UPDATE, DROP, DELETE ON DATABASE management TO adminapi;
|
||||
GRANT SELECT, INSERT, UPDATE, DROP, DELETE ON DATABASE notification TO adminapi;
|
||||
|
||||
CREATE USER auth;
|
||||
CREATE USER auth WITH PASSWORD ${authpassword};
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON DATABASE auth TO auth;
|
||||
GRANT SELECT, INSERT ON DATABASE eventstore TO auth;
|
||||
|
||||
CREATE USER notification;
|
||||
CREATE USER notification WITH PASSWORD ${notificationpassword};
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON DATABASE notification TO notification;
|
||||
GRANT SELECT, INSERT ON DATABASE eventstore TO notification;
|
||||
|
||||
CREATE USER authz;
|
||||
CREATE USER authz WITH PASSWORD ${authzpassword};
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON DATABASE authz TO authz;
|
||||
GRANT SELECT, INSERT ON DATABASE eventstore TO authz;
|
||||
GRANT SELECT, INSERT, UPDATE ON DATABASE auth TO authz;
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
ALTER TABLE management.users ADD COLUMN machine_name STRING, ADD COLUMN machine_description STRING, ADD COLUMN user_type STRING;
|
||||
ALTER TABLE adminapi.users ADD COLUMN machine_name STRING, ADD COLUMN machine_description STRING, ADD COLUMN user_type STRING;
|
||||
ALTER TABLE auth.users ADD COLUMN machine_name STRING, ADD COLUMN machine_description STRING, ADD COLUMN user_type STRING;
|
||||
@ -12,4 +13,4 @@ CREATE TABLE management.machine_keys (
|
||||
creation_date TIMESTAMPTZ,
|
||||
|
||||
PRIMARY KEY (id, user_id)
|
||||
)
|
||||
)
|
@ -1,3 +1,4 @@
|
||||
|
||||
ALTER TABLE adminapi.idp_configs ADD COLUMN oidc_idp_display_name_mapping SMALLINT;
|
||||
ALTER TABLE adminapi.idp_configs ADD COLUMN oidc_idp_username_mapping SMALLINT;
|
||||
|
||||
@ -27,7 +28,6 @@ CREATE TABLE auth.idp_configs (
|
||||
PRIMARY KEY (idp_config_id)
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE auth.login_policies (
|
||||
aggregate_id TEXT,
|
||||
|
||||
@ -58,7 +58,6 @@ CREATE TABLE auth.idp_providers (
|
||||
PRIMARY KEY (aggregate_id, idp_config_id)
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE auth.user_external_idps (
|
||||
external_user_id TEXT,
|
||||
idp_config_id TEXT,
|
||||
@ -88,7 +87,7 @@ CREATE TABLE management.user_external_idps (
|
||||
|
||||
PRIMARY KEY (external_user_id, idp_config_id)
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE adminapi.user_external_idps (
|
||||
idp_config_id TEXT,
|
||||
external_user_id TEXT,
|
||||
@ -102,4 +101,4 @@ CREATE TABLE adminapi.user_external_idps (
|
||||
resource_owner TEXT,
|
||||
|
||||
PRIMARY KEY (external_user_id, idp_config_id)
|
||||
);
|
||||
);
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
CREATE SEQUENCE eventstore.event_seq;
|
||||
|
||||
GRANT UPDATE ON TABLE eventstore.event_seq TO management;
|
||||
@ -27,3 +28,4 @@ CREATE TABLE eventstore.events (
|
||||
INDEX agg_type_agg_id (aggregate_type, aggregate_id),
|
||||
CONSTRAINT previous_sequence_unique UNIQUE (previous_sequence DESC)
|
||||
);
|
||||
ALTER SEQUENCE eventstore.event_seq OWNED BY eventstore.events.event_sequence;
|
||||
|
82
migrations/cockroach/V1.27__database_and_table_ownership.sql
Normal file
82
migrations/cockroach/V1.27__database_and_table_ownership.sql
Normal file
@ -0,0 +1,82 @@
|
||||
ALTER DATABASE adminapi OWNER TO admin;
|
||||
ALTER DATABASE auth OWNER TO admin;
|
||||
ALTER DATABASE authz OWNER TO admin;
|
||||
ALTER DATABASE eventstore OWNER TO admin;
|
||||
ALTER DATABASE management OWNER TO admin;
|
||||
ALTER DATABASE notification OWNER TO admin;
|
||||
ALTER DATABASE defaultdb OWNER TO admin;
|
||||
ALTER DATABASE postgres OWNER TO admin;
|
||||
ALTER TABLE eventstore.events OWNER TO admin;
|
||||
ALTER TABLE management.locks OWNER TO admin;
|
||||
ALTER TABLE management.current_sequences OWNER TO admin;
|
||||
ALTER TABLE management.failed_events OWNER TO admin;
|
||||
ALTER TABLE management.projects OWNER TO admin;
|
||||
ALTER TABLE management.project_grants OWNER TO admin;
|
||||
ALTER TABLE management.project_roles OWNER TO admin;
|
||||
ALTER TABLE management.project_members OWNER TO admin;
|
||||
ALTER TABLE management.project_grant_members OWNER TO admin;
|
||||
ALTER TABLE management.applications OWNER TO admin;
|
||||
ALTER TABLE management.users OWNER TO admin;
|
||||
ALTER TABLE management.user_grants OWNER TO admin;
|
||||
ALTER TABLE management.org_domains OWNER TO admin;
|
||||
ALTER TABLE auth.locks OWNER TO admin;
|
||||
ALTER TABLE auth.current_sequences OWNER TO admin;
|
||||
ALTER TABLE auth.failed_events OWNER TO admin;
|
||||
ALTER TABLE auth.auth_requests OWNER TO admin;
|
||||
ALTER TABLE auth.users OWNER TO admin;
|
||||
ALTER TABLE auth.user_sessions OWNER TO admin;
|
||||
ALTER TABLE auth.tokens OWNER TO admin;
|
||||
ALTER TABLE notification.locks OWNER TO admin;
|
||||
ALTER TABLE notification.current_sequences OWNER TO admin;
|
||||
ALTER TABLE notification.failed_events OWNER TO admin;
|
||||
ALTER TABLE notification.notify_users OWNER TO admin;
|
||||
ALTER TABLE adminapi.orgs OWNER TO admin;
|
||||
ALTER TABLE adminapi.failed_events OWNER TO admin;
|
||||
ALTER TABLE adminapi.locks OWNER TO admin;
|
||||
ALTER TABLE adminapi.current_sequences OWNER TO admin;
|
||||
ALTER TABLE adminapi.iam_members OWNER TO admin;
|
||||
ALTER TABLE management.orgs OWNER TO admin;
|
||||
ALTER TABLE management.org_members OWNER TO admin;
|
||||
ALTER TABLE auth.keys OWNER TO admin;
|
||||
ALTER TABLE auth.applications OWNER TO admin;
|
||||
ALTER TABLE auth.user_grants OWNER TO admin;
|
||||
ALTER TABLE auth.orgs OWNER TO admin;
|
||||
ALTER TABLE authz.locks OWNER TO admin;
|
||||
ALTER TABLE authz.current_sequences OWNER TO admin;
|
||||
ALTER TABLE authz.failed_events OWNER TO admin;
|
||||
ALTER TABLE authz.user_grants OWNER TO admin;
|
||||
ALTER TABLE authz.applications OWNER TO admin;
|
||||
ALTER TABLE authz.orgs OWNER TO admin;
|
||||
ALTER TABLE management.user_memberships OWNER TO admin;
|
||||
ALTER TABLE adminapi.users OWNER TO admin;
|
||||
ALTER TABLE adminapi.idp_configs OWNER TO admin;
|
||||
ALTER TABLE management.idp_configs OWNER TO admin;
|
||||
ALTER TABLE adminapi.login_policies OWNER TO admin;
|
||||
ALTER TABLE management.login_policies OWNER TO admin;
|
||||
ALTER TABLE adminapi.idp_providers OWNER TO admin;
|
||||
ALTER TABLE management.idp_providers OWNER TO admin;
|
||||
ALTER TABLE management.machine_keys OWNER TO admin;
|
||||
ALTER TABLE auth.user_memberships OWNER TO admin;
|
||||
ALTER TABLE auth.machine_keys OWNER TO admin;
|
||||
ALTER TABLE auth.idp_configs OWNER TO admin;
|
||||
ALTER TABLE auth.login_policies OWNER TO admin;
|
||||
ALTER TABLE auth.idp_providers OWNER TO admin;
|
||||
ALTER TABLE auth.user_external_idps OWNER TO admin;
|
||||
ALTER TABLE management.user_external_idps OWNER TO admin;
|
||||
ALTER TABLE adminapi.user_external_idps OWNER TO admin;
|
||||
ALTER TABLE auth.password_complexity_policies OWNER TO admin;
|
||||
ALTER TABLE adminapi.password_complexity_policies OWNER TO admin;
|
||||
ALTER TABLE management.password_complexity_policies OWNER TO admin;
|
||||
ALTER TABLE auth.password_age_policies OWNER TO admin;
|
||||
ALTER TABLE adminapi.password_age_policies OWNER TO admin;
|
||||
ALTER TABLE management.password_age_policies OWNER TO admin;
|
||||
ALTER TABLE auth.password_lockout_policies OWNER TO admin;
|
||||
ALTER TABLE adminapi.password_lockout_policies OWNER TO admin;
|
||||
ALTER TABLE management.password_lockout_policies OWNER TO admin;
|
||||
ALTER TABLE management.org_iam_policies OWNER TO admin;
|
||||
ALTER TABLE auth.org_iam_policies OWNER TO admin;
|
||||
ALTER TABLE adminapi.org_iam_policies OWNER TO admin;
|
||||
ALTER TABLE auth.project_roles OWNER TO admin;
|
||||
ALTER TABLE adminapi.label_policies OWNER TO admin;
|
||||
ALTER TABLE management.label_policies OWNER TO admin;
|
||||
ALTER TABLE defaultdb.flyway_schema_history OWNER TO admin;
|
@ -75,4 +75,9 @@ GRANT SELECT ON TABLE management.mail_texts TO notification;
|
||||
|
||||
|
||||
ALTER TABLE management.project_roles ADD COLUMN change_date TIMESTAMPTZ;
|
||||
ALTER TABLE auth.project_roles ADD COLUMN change_date TIMESTAMPTZ;
|
||||
ALTER TABLE auth.project_roles ADD COLUMN change_date TIMESTAMPTZ;
|
||||
|
||||
ALTER TABLE management.mail_texts OWNER TO admin;
|
||||
ALTER TABLE adminapi.mail_texts OWNER TO admin;
|
||||
ALTER TABLE management.mail_templates OWNER TO admin;
|
||||
ALTER TABLE adminapi.mail_templates OWNER TO admin;
|
@ -1,5 +0,0 @@
|
||||
//+build ignore
|
||||
|
||||
package migrations
|
||||
|
||||
//go:generate flyway -url=jdbc:postgresql://cockroachdb-public:26257/defaultdb -user=root -password= -locations=filesystem:./ clean
|
@ -1,5 +0,0 @@
|
||||
//+build ignore
|
||||
|
||||
package migrations
|
||||
|
||||
//go:generate flyway -url=jdbc:postgresql://localhost:26257/defaultdb -user=root -password= -locations=filesystem:./ clean
|
@ -1,5 +0,0 @@
|
||||
//+build ignore
|
||||
|
||||
package migrations
|
||||
|
||||
//go:generate flyway -url=jdbc:postgresql://cockroachdb-public:26257/defaultdb -user=root -password= -locations=filesystem:./ migrate
|
@ -1,5 +0,0 @@
|
||||
//+build ignore
|
||||
|
||||
package migrations
|
||||
|
||||
//go:generate flyway -url=jdbc:postgresql://localhost:26257/defaultdb -user=root -password= -locations=filesystem:./ migrate
|
1
migrations/testfiles/1.2__not.sql
Normal file
1
migrations/testfiles/1.2__not.sql
Normal file
@ -0,0 +1 @@
|
||||
not
|
1
migrations/testfiles/V1.1__test.sql
Normal file
1
migrations/testfiles/V1.1__test.sql
Normal file
@ -0,0 +1 @@
|
||||
test
|
1
migrations/testfiles/V1.2__test2.sql
Normal file
1
migrations/testfiles/V1.2__test2.sql
Normal file
@ -0,0 +1 @@
|
||||
test2
|
0
migrations/testfiles/V1.3__empty.sql
Normal file
0
migrations/testfiles/V1.3__empty.sql
Normal file
113
operator/adapt.go
Normal file
113
operator/adapt.go
Normal file
@ -0,0 +1,113 @@
|
||||
package operator
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/git"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/orbos/pkg/kubernetes/resources"
|
||||
"github.com/caos/orbos/pkg/secret"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type AdaptFunc func(monitor mntr.Monitor, desired *tree.Tree, current *tree.Tree) (QueryFunc, DestroyFunc, map[string]*secret.Secret, error)
|
||||
|
||||
type EnsureFunc func(k8sClient kubernetes.ClientInt) error
|
||||
|
||||
type DestroyFunc func(k8sClient kubernetes.ClientInt) error
|
||||
|
||||
type QueryFunc func(k8sClient kubernetes.ClientInt, queried map[string]interface{}) (EnsureFunc, error)
|
||||
|
||||
func Parse(gitClient *git.Client, file string) (*tree.Tree, error) {
|
||||
if err := gitClient.Clone(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tree := &tree.Tree{}
|
||||
if err := yaml.Unmarshal(gitClient.Read(file), tree); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tree, nil
|
||||
}
|
||||
|
||||
func ResourceDestroyToZitadelDestroy(destroyFunc resources.DestroyFunc) DestroyFunc {
|
||||
return func(k8sClient kubernetes.ClientInt) error {
|
||||
return destroyFunc(k8sClient)
|
||||
}
|
||||
}
|
||||
|
||||
func ResourceQueryToZitadelQuery(queryFunc resources.QueryFunc) QueryFunc {
|
||||
return func(k8sClient kubernetes.ClientInt, _ map[string]interface{}) (EnsureFunc, error) {
|
||||
ensure, err := queryFunc(k8sClient)
|
||||
ensureInternal := ResourceEnsureToZitadelEnsure(ensure)
|
||||
|
||||
return func(k8sClient kubernetes.ClientInt) error {
|
||||
return ensureInternal(k8sClient)
|
||||
}, err
|
||||
}
|
||||
}
|
||||
|
||||
func ResourceEnsureToZitadelEnsure(ensureFunc resources.EnsureFunc) EnsureFunc {
|
||||
return func(k8sClient kubernetes.ClientInt) error {
|
||||
return ensureFunc(k8sClient)
|
||||
}
|
||||
}
|
||||
func EnsureFuncToQueryFunc(ensure EnsureFunc) QueryFunc {
|
||||
return func(k8sClient kubernetes.ClientInt, queried map[string]interface{}) (ensureFunc EnsureFunc, err error) {
|
||||
return ensure, err
|
||||
}
|
||||
}
|
||||
|
||||
func QueriersToEnsureFunc(monitor mntr.Monitor, infoLogs bool, queriers []QueryFunc, k8sClient kubernetes.ClientInt, queried map[string]interface{}) (EnsureFunc, error) {
|
||||
if infoLogs {
|
||||
monitor.Info("querying...")
|
||||
} else {
|
||||
monitor.Debug("querying...")
|
||||
}
|
||||
ensurers := make([]EnsureFunc, 0)
|
||||
for _, querier := range queriers {
|
||||
ensurer, err := querier(k8sClient, queried)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error while querying")
|
||||
}
|
||||
ensurers = append(ensurers, ensurer)
|
||||
}
|
||||
if infoLogs {
|
||||
monitor.Info("queried")
|
||||
} else {
|
||||
monitor.Debug("queried")
|
||||
}
|
||||
return func(k8sClient kubernetes.ClientInt) error {
|
||||
if infoLogs {
|
||||
monitor.Info("ensuring...")
|
||||
} else {
|
||||
monitor.Debug("ensuring...")
|
||||
}
|
||||
for _, ensurer := range ensurers {
|
||||
if err := ensurer(k8sClient); err != nil {
|
||||
return errors.Wrap(err, "error while ensuring")
|
||||
}
|
||||
}
|
||||
if infoLogs {
|
||||
monitor.Info("ensured")
|
||||
} else {
|
||||
monitor.Debug("ensured")
|
||||
}
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DestroyersToDestroyFunc(monitor mntr.Monitor, destroyers []DestroyFunc) DestroyFunc {
|
||||
return func(k8sClient kubernetes.ClientInt) error {
|
||||
monitor.Info("destroying...")
|
||||
for _, destroyer := range destroyers {
|
||||
if err := destroyer(k8sClient); err != nil {
|
||||
return errors.Wrap(err, "error while destroying")
|
||||
}
|
||||
}
|
||||
monitor.Info("destroyed")
|
||||
return nil
|
||||
}
|
||||
}
|
83
operator/api/api.go
Normal file
83
operator/api/api.go
Normal file
@ -0,0 +1,83 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/git"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/caos/zitadel/operator/common"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
zitadelFile = "zitadel.yml"
|
||||
databaseFile = "database.yml"
|
||||
)
|
||||
|
||||
type PushDesiredFunc func(monitor mntr.Monitor) error
|
||||
|
||||
func ExistsZitadelYml(gitClient *git.Client) (bool, error) {
|
||||
return existsFileInGit(gitClient, zitadelFile)
|
||||
}
|
||||
|
||||
func ReadZitadelYml(gitClient *git.Client) (*tree.Tree, error) {
|
||||
return readFileInGit(gitClient, zitadelFile)
|
||||
}
|
||||
|
||||
func PushZitadelYml(monitor mntr.Monitor, msg string, gitClient *git.Client, desired *tree.Tree) (err error) {
|
||||
return pushFileInGit(monitor, msg, gitClient, desired, zitadelFile)
|
||||
}
|
||||
|
||||
func PushZitadelDesiredFunc(gitClient *git.Client, desired *tree.Tree) PushDesiredFunc {
|
||||
return func(monitor mntr.Monitor) error {
|
||||
monitor.Info("Writing zitadel desired state")
|
||||
return PushZitadelYml(monitor, "Zitadel desired state written", gitClient, desired)
|
||||
}
|
||||
}
|
||||
|
||||
func ExistsDatabaseYml(gitClient *git.Client) (bool, error) {
|
||||
return existsFileInGit(gitClient, databaseFile)
|
||||
}
|
||||
|
||||
func ReadDatabaseYml(gitClient *git.Client) (*tree.Tree, error) {
|
||||
return readFileInGit(gitClient, databaseFile)
|
||||
}
|
||||
|
||||
func PushDatabaseYml(monitor mntr.Monitor, msg string, gitClient *git.Client, desired *tree.Tree) (err error) {
|
||||
return pushFileInGit(monitor, msg, gitClient, desired, databaseFile)
|
||||
}
|
||||
|
||||
func PushDatabaseDesiredFunc(gitClient *git.Client, desired *tree.Tree) PushDesiredFunc {
|
||||
return func(monitor mntr.Monitor) error {
|
||||
monitor.Info("Writing database desired state")
|
||||
return PushDatabaseYml(monitor, "Database desired state written", gitClient, desired)
|
||||
}
|
||||
}
|
||||
|
||||
func pushFileInGit(monitor mntr.Monitor, msg string, gitClient *git.Client, desired *tree.Tree, path string) (err error) {
|
||||
monitor.OnChange = func(_ string, fields map[string]string) {
|
||||
err = gitClient.UpdateRemote(mntr.SprintCommit(msg, fields), git.File{
|
||||
Path: path,
|
||||
Content: common.MarshalYAML(desired),
|
||||
})
|
||||
mntr.LogMessage(msg, fields)
|
||||
}
|
||||
monitor.Changed(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
func existsFileInGit(gitClient *git.Client, path string) (bool, error) {
|
||||
of := gitClient.Read(path)
|
||||
if of != nil && len(of) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func readFileInGit(gitClient *git.Client, path string) (*tree.Tree, error) {
|
||||
tree := &tree.Tree{}
|
||||
if err := yaml.Unmarshal(gitClient.Read(path), tree); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tree, nil
|
||||
}
|
25
operator/common/yaml.go
Normal file
25
operator/common/yaml.go
Normal file
@ -0,0 +1,25 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func MarshalYAML(sth interface{}) []byte {
|
||||
if sth == nil {
|
||||
return nil
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
encoder := yaml.NewEncoder(buf)
|
||||
|
||||
defer func() {
|
||||
encoder.Close()
|
||||
buf.Truncate(0)
|
||||
}()
|
||||
|
||||
encoder.SetIndent(2)
|
||||
if err := encoder.Encode(sth); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
71
operator/database/kinds/backups/backups.go
Normal file
71
operator/database/kinds/backups/backups.go
Normal file
@ -0,0 +1,71 @@
|
||||
package backups
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/caos/orbos/pkg/secret"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/caos/zitadel/operator"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket"
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func GetQueryAndDestroyFuncs(
|
||||
monitor mntr.Monitor,
|
||||
desiredTree *tree.Tree,
|
||||
currentTree *tree.Tree,
|
||||
name string,
|
||||
namespace string,
|
||||
componentLabels *labels.Component,
|
||||
checkDBReady operator.EnsureFunc,
|
||||
timestamp string,
|
||||
nodeselector map[string]string,
|
||||
tolerations []corev1.Toleration,
|
||||
version string,
|
||||
features []string,
|
||||
) (
|
||||
operator.QueryFunc,
|
||||
operator.DestroyFunc,
|
||||
map[string]*secret.Secret,
|
||||
error,
|
||||
) {
|
||||
switch desiredTree.Common.Kind {
|
||||
case "databases.caos.ch/BucketBackup":
|
||||
return bucket.AdaptFunc(
|
||||
name,
|
||||
namespace,
|
||||
labels.MustForComponent(
|
||||
labels.MustReplaceAPI(
|
||||
labels.GetAPIFromComponent(componentLabels),
|
||||
"BucketBackup",
|
||||
desiredTree.Common.Version,
|
||||
),
|
||||
"backup"),
|
||||
checkDBReady,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
version,
|
||||
features,
|
||||
)(monitor, desiredTree, currentTree)
|
||||
default:
|
||||
return nil, nil, nil, errors.Errorf("unknown database kind %s", desiredTree.Common.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
func GetBackupList(
|
||||
monitor mntr.Monitor,
|
||||
name string,
|
||||
desiredTree *tree.Tree,
|
||||
) (
|
||||
[]string,
|
||||
error,
|
||||
) {
|
||||
switch desiredTree.Common.Kind {
|
||||
case "databases.caos.ch/BucketBackup":
|
||||
return bucket.BackupList()(monitor, name, desiredTree)
|
||||
default:
|
||||
return nil, errors.Errorf("unknown database kind %s", desiredTree.Common.Kind)
|
||||
}
|
||||
}
|
230
operator/database/kinds/backups/bucket/adapt.go
Normal file
230
operator/database/kinds/backups/bucket/adapt.go
Normal file
@ -0,0 +1,230 @@
|
||||
package bucket
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/orbos/pkg/kubernetes/resources/secret"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
secretpkg "github.com/caos/orbos/pkg/secret"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/caos/zitadel/operator"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket/backup"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket/clean"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket/restore"
|
||||
coreDB "github.com/caos/zitadel/operator/database/kinds/databases/core"
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
secretName = "backup-serviceaccountjson"
|
||||
secretKey = "serviceaccountjson"
|
||||
)
|
||||
|
||||
func AdaptFunc(
|
||||
name string,
|
||||
namespace string,
|
||||
componentLabels *labels.Component,
|
||||
checkDBReady operator.EnsureFunc,
|
||||
timestamp string,
|
||||
nodeselector map[string]string,
|
||||
tolerations []corev1.Toleration,
|
||||
version string,
|
||||
features []string,
|
||||
) operator.AdaptFunc {
|
||||
return func(monitor mntr.Monitor, desired *tree.Tree, current *tree.Tree) (queryFunc operator.QueryFunc, destroyFunc operator.DestroyFunc, secrets map[string]*secretpkg.Secret, err error) {
|
||||
|
||||
internalMonitor := monitor.WithField("component", "backup")
|
||||
|
||||
desiredKind, err := ParseDesiredV0(desired)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(err, "parsing desired state failed")
|
||||
}
|
||||
desired.Parsed = desiredKind
|
||||
|
||||
if !monitor.IsVerbose() && desiredKind.Spec.Verbose {
|
||||
internalMonitor.Verbose()
|
||||
}
|
||||
|
||||
destroyS, err := secret.AdaptFuncToDestroy(namespace, secretName)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
queryS, err := secret.AdaptFuncToEnsure(namespace, labels.MustForName(componentLabels, secretName), map[string]string{secretKey: desiredKind.Spec.ServiceAccountJSON.Value})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
_, destroyB, err := backup.AdaptFunc(
|
||||
internalMonitor,
|
||||
name,
|
||||
namespace,
|
||||
componentLabels,
|
||||
[]string{},
|
||||
checkDBReady,
|
||||
desiredKind.Spec.Bucket,
|
||||
desiredKind.Spec.Cron,
|
||||
secretName,
|
||||
secretKey,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
features,
|
||||
version,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
_, destroyR, err := restore.AdaptFunc(
|
||||
monitor,
|
||||
name,
|
||||
namespace,
|
||||
componentLabels,
|
||||
[]string{},
|
||||
desiredKind.Spec.Bucket,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
checkDBReady,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
_, destroyC, err := clean.AdaptFunc(
|
||||
monitor,
|
||||
name,
|
||||
namespace,
|
||||
componentLabels,
|
||||
[]string{},
|
||||
nodeselector,
|
||||
tolerations,
|
||||
checkDBReady,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
destroyers := make([]operator.DestroyFunc, 0)
|
||||
for _, feature := range features {
|
||||
switch feature {
|
||||
case backup.Normal, backup.Instant:
|
||||
destroyers = append(destroyers,
|
||||
operator.ResourceDestroyToZitadelDestroy(destroyS),
|
||||
destroyB,
|
||||
)
|
||||
case clean.Instant:
|
||||
destroyers = append(destroyers,
|
||||
destroyC,
|
||||
)
|
||||
case restore.Instant:
|
||||
destroyers = append(destroyers,
|
||||
destroyR,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return func(k8sClient kubernetes.ClientInt, queried map[string]interface{}) (operator.EnsureFunc, error) {
|
||||
currentDB, err := coreDB.ParseQueriedForDatabase(queried)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databases, err := currentDB.GetListDatabasesFunc()(k8sClient)
|
||||
if err != nil {
|
||||
databases = []string{}
|
||||
}
|
||||
|
||||
queryB, _, err := backup.AdaptFunc(
|
||||
internalMonitor,
|
||||
name,
|
||||
namespace,
|
||||
componentLabels,
|
||||
databases,
|
||||
checkDBReady,
|
||||
desiredKind.Spec.Bucket,
|
||||
desiredKind.Spec.Cron,
|
||||
secretName,
|
||||
secretKey,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
features,
|
||||
version,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queryR, _, err := restore.AdaptFunc(
|
||||
monitor,
|
||||
name,
|
||||
namespace,
|
||||
componentLabels,
|
||||
databases,
|
||||
desiredKind.Spec.Bucket,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
checkDBReady,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queryC, _, err := clean.AdaptFunc(
|
||||
monitor,
|
||||
name,
|
||||
namespace,
|
||||
componentLabels,
|
||||
databases,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
checkDBReady,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queriers := make([]operator.QueryFunc, 0)
|
||||
if databases != nil && len(databases) != 0 {
|
||||
for _, feature := range features {
|
||||
switch feature {
|
||||
case backup.Normal, backup.Instant:
|
||||
queriers = append(queriers,
|
||||
operator.ResourceQueryToZitadelQuery(queryS),
|
||||
queryB,
|
||||
)
|
||||
case clean.Instant:
|
||||
queriers = append(queriers,
|
||||
queryC,
|
||||
)
|
||||
case restore.Instant:
|
||||
queriers = append(queriers,
|
||||
queryR,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return operator.QueriersToEnsureFunc(internalMonitor, false, queriers, k8sClient, queried)
|
||||
},
|
||||
operator.DestroyersToDestroyFunc(internalMonitor, destroyers),
|
||||
getSecretsMap(desiredKind),
|
||||
nil
|
||||
}
|
||||
}
|
372
operator/database/kinds/backups/bucket/adapt_test.go
Normal file
372
operator/database/kinds/backups/bucket/adapt_test.go
Normal file
@ -0,0 +1,372 @@
|
||||
package bucket
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
kubernetesmock "github.com/caos/orbos/pkg/kubernetes/mock"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/caos/orbos/pkg/secret"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket/backup"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket/clean"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket/restore"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBucket_Secrets(t *testing.T) {
|
||||
masterkey := "testMk"
|
||||
features := []string{backup.Normal}
|
||||
saJson := "testSA"
|
||||
|
||||
bucketName := "testBucket2"
|
||||
cron := "testCron2"
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs2"
|
||||
|
||||
kindVersion := "v0"
|
||||
kind := "BucketBackup"
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "BucketBackup", kindVersion), "testComponent")
|
||||
|
||||
timestamp := "test2"
|
||||
nodeselector := map[string]string{"test2": "test2"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey2", Operator: "testOp2"}}
|
||||
backupName := "testName2"
|
||||
version := "testVersion2"
|
||||
|
||||
desired := getDesiredTree(t, masterkey, &DesiredV0{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/" + kind,
|
||||
Version: kindVersion,
|
||||
},
|
||||
Spec: &Spec{
|
||||
Verbose: true,
|
||||
Cron: cron,
|
||||
Bucket: bucketName,
|
||||
ServiceAccountJSON: &secret.Secret{
|
||||
Value: saJson,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
checkDBReady := func(k8sClient kubernetes.ClientInt) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
allSecrets := map[string]string{
|
||||
"serviceaccountjson": saJson,
|
||||
}
|
||||
|
||||
_, _, secrets, err := AdaptFunc(
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
checkDBReady,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
version,
|
||||
features,
|
||||
)(
|
||||
monitor,
|
||||
desired,
|
||||
&tree.Tree{},
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
for key, value := range allSecrets {
|
||||
assert.Contains(t, secrets, key)
|
||||
assert.Equal(t, value, secrets[key].Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBucket_AdaptBackup(t *testing.T) {
|
||||
masterkey := "testMk"
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
features := []string{backup.Normal}
|
||||
saJson := "testSA"
|
||||
|
||||
bucketName := "testBucket2"
|
||||
cron := "testCron2"
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs2"
|
||||
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "BucketBackup", "v0"), "testComponent")
|
||||
k8sLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "testComponent",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": "backup-serviceaccountjson",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"app.kubernetes.io/version": "testVersion",
|
||||
"caos.ch/apiversion": "v0",
|
||||
"caos.ch/kind": "BucketBackup",
|
||||
}
|
||||
timestamp := "test2"
|
||||
nodeselector := map[string]string{"test2": "test2"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey2", Operator: "testOp2"}}
|
||||
backupName := "testName2"
|
||||
version := "testVersion2"
|
||||
|
||||
desired := getDesiredTree(t, masterkey, &DesiredV0{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/BucketBackup",
|
||||
Version: "v0",
|
||||
},
|
||||
Spec: &Spec{
|
||||
Verbose: true,
|
||||
Cron: cron,
|
||||
Bucket: bucketName,
|
||||
ServiceAccountJSON: &secret.Secret{
|
||||
Value: saJson,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
checkDBReady := func(k8sClient kubernetes.ClientInt) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
SetBackup(client, namespace, k8sLabels, saJson)
|
||||
|
||||
query, _, _, err := AdaptFunc(
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
checkDBReady,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
version,
|
||||
features,
|
||||
)(
|
||||
monitor,
|
||||
desired,
|
||||
&tree.Tree{},
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
databases := []string{"test1", "test2"}
|
||||
queried := SetQueriedForDatabases(databases)
|
||||
ensure, err := query(client, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ensure)
|
||||
assert.NoError(t, ensure(client))
|
||||
}
|
||||
|
||||
func TestBucket_AdaptInstantBackup(t *testing.T) {
|
||||
masterkey := "testMk"
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
features := []string{backup.Instant}
|
||||
|
||||
bucketName := "testBucket1"
|
||||
cron := "testCron"
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs"
|
||||
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "BucketBackup", "v0"), "testComponent")
|
||||
k8sLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "testComponent",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": "backup-serviceaccountjson",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"app.kubernetes.io/version": "testVersion",
|
||||
"caos.ch/apiversion": "v0",
|
||||
"caos.ch/kind": "BucketBackup",
|
||||
}
|
||||
timestamp := "test"
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey", Operator: "testOp"}}
|
||||
backupName := "testName"
|
||||
version := "testVersion"
|
||||
saJson := "testSA"
|
||||
|
||||
desired := getDesiredTree(t, masterkey, &DesiredV0{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/BucketBackup",
|
||||
Version: "v0",
|
||||
},
|
||||
Spec: &Spec{
|
||||
Verbose: true,
|
||||
Cron: cron,
|
||||
Bucket: bucketName,
|
||||
ServiceAccountJSON: &secret.Secret{
|
||||
Value: saJson,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
checkDBReady := func(k8sClient kubernetes.ClientInt) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
SetInstantBackup(client, namespace, backupName, k8sLabels, saJson)
|
||||
|
||||
query, _, _, err := AdaptFunc(
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
checkDBReady,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
version,
|
||||
features,
|
||||
)(
|
||||
monitor,
|
||||
desired,
|
||||
&tree.Tree{},
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
databases := []string{"test1", "test2"}
|
||||
queried := SetQueriedForDatabases(databases)
|
||||
ensure, err := query(client, queried)
|
||||
assert.NotNil(t, ensure)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, ensure(client))
|
||||
}
|
||||
|
||||
func TestBucket_AdaptRestore(t *testing.T) {
|
||||
masterkey := "testMk"
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
features := []string{restore.Instant}
|
||||
|
||||
bucketName := "testBucket1"
|
||||
cron := "testCron"
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs"
|
||||
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "BucketBackup", "v0"), "testComponent")
|
||||
k8sLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "testComponent",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": "backup-serviceaccountjson",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"app.kubernetes.io/version": "testVersion",
|
||||
"caos.ch/apiversion": "v0",
|
||||
"caos.ch/kind": "BucketBackup",
|
||||
}
|
||||
|
||||
timestamp := "test"
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey", Operator: "testOp"}}
|
||||
backupName := "testName"
|
||||
version := "testVersion"
|
||||
saJson := "testSA"
|
||||
|
||||
desired := getDesiredTree(t, masterkey, &DesiredV0{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/BucketBackup",
|
||||
Version: "v0",
|
||||
},
|
||||
Spec: &Spec{
|
||||
Verbose: true,
|
||||
Cron: cron,
|
||||
Bucket: bucketName,
|
||||
ServiceAccountJSON: &secret.Secret{
|
||||
Value: saJson,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
checkDBReady := func(k8sClient kubernetes.ClientInt) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
SetRestore(client, namespace, backupName, k8sLabels, saJson)
|
||||
|
||||
query, _, _, err := AdaptFunc(
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
checkDBReady,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
version,
|
||||
features,
|
||||
)(
|
||||
monitor,
|
||||
desired,
|
||||
&tree.Tree{},
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
databases := []string{"test1", "test2"}
|
||||
queried := SetQueriedForDatabases(databases)
|
||||
ensure, err := query(client, queried)
|
||||
assert.NotNil(t, ensure)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, ensure(client))
|
||||
}
|
||||
|
||||
func TestBucket_AdaptClean(t *testing.T) {
|
||||
masterkey := "testMk"
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
features := []string{clean.Instant}
|
||||
|
||||
bucketName := "testBucket1"
|
||||
cron := "testCron"
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs"
|
||||
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "BucketBackup", "v0"), "testComponent")
|
||||
|
||||
timestamp := "test"
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey", Operator: "testOp"}}
|
||||
backupName := "testName"
|
||||
version := "testVersion"
|
||||
saJson := "testSA"
|
||||
|
||||
desired := getDesiredTree(t, masterkey, &DesiredV0{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/BucketBackup",
|
||||
Version: "v0",
|
||||
},
|
||||
Spec: &Spec{
|
||||
Verbose: true,
|
||||
Cron: cron,
|
||||
Bucket: bucketName,
|
||||
ServiceAccountJSON: &secret.Secret{
|
||||
Value: saJson,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
checkDBReady := func(k8sClient kubernetes.ClientInt) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
SetClean(client, namespace, backupName)
|
||||
|
||||
query, _, _, err := AdaptFunc(
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
checkDBReady,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
version,
|
||||
features,
|
||||
)(
|
||||
monitor,
|
||||
desired,
|
||||
&tree.Tree{},
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
databases := []string{"test1", "test2"}
|
||||
queried := SetQueriedForDatabases(databases)
|
||||
ensure, err := query(client, queried)
|
||||
assert.NotNil(t, ensure)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, ensure(client))
|
||||
}
|
136
operator/database/kinds/backups/bucket/backup/adapt.go
Normal file
136
operator/database/kinds/backups/bucket/backup/adapt.go
Normal file
@ -0,0 +1,136 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/operator"
|
||||
"time"
|
||||
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/orbos/pkg/kubernetes/resources/cronjob"
|
||||
"github.com/caos/orbos/pkg/kubernetes/resources/job"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultMode int32 = 256
|
||||
certPath = "/cockroach/cockroach-certs"
|
||||
secretPath = "/secrets/sa.json"
|
||||
backupPath = "/cockroach"
|
||||
backupNameEnv = "BACKUP_NAME"
|
||||
cronJobNamePrefix = "backup-"
|
||||
internalSecretName = "client-certs"
|
||||
image = "ghcr.io/caos/zitadel-crbackup"
|
||||
rootSecretName = "cockroachdb.client.root"
|
||||
timeout time.Duration = 60
|
||||
Normal = "backup"
|
||||
Instant = "instantbackup"
|
||||
)
|
||||
|
||||
func AdaptFunc(
|
||||
monitor mntr.Monitor,
|
||||
backupName string,
|
||||
namespace string,
|
||||
componentLabels *labels.Component,
|
||||
databases []string,
|
||||
checkDBReady operator.EnsureFunc,
|
||||
bucketName string,
|
||||
cron string,
|
||||
secretName string,
|
||||
secretKey string,
|
||||
timestamp string,
|
||||
nodeselector map[string]string,
|
||||
tolerations []corev1.Toleration,
|
||||
features []string,
|
||||
version string,
|
||||
) (
|
||||
queryFunc operator.QueryFunc,
|
||||
destroyFunc operator.DestroyFunc,
|
||||
err error,
|
||||
) {
|
||||
|
||||
command := getBackupCommand(
|
||||
timestamp,
|
||||
databases,
|
||||
bucketName,
|
||||
backupName,
|
||||
)
|
||||
|
||||
jobSpecDef := getJobSpecDef(
|
||||
nodeselector,
|
||||
tolerations,
|
||||
secretName,
|
||||
secretKey,
|
||||
backupName,
|
||||
version,
|
||||
command,
|
||||
)
|
||||
|
||||
destroyers := []operator.DestroyFunc{}
|
||||
queriers := []operator.QueryFunc{}
|
||||
|
||||
cronJobDef := getCronJob(
|
||||
namespace,
|
||||
labels.MustForName(componentLabels, GetJobName(backupName)),
|
||||
cron,
|
||||
jobSpecDef,
|
||||
)
|
||||
|
||||
destroyCJ, err := cronjob.AdaptFuncToDestroy(cronJobDef.Namespace, cronJobDef.Name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
queryCJ, err := cronjob.AdaptFuncToEnsure(cronJobDef)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
jobDef := getJob(
|
||||
namespace,
|
||||
labels.MustForName(componentLabels, cronJobNamePrefix+backupName),
|
||||
jobSpecDef,
|
||||
)
|
||||
|
||||
destroyJ, err := job.AdaptFuncToDestroy(jobDef.Namespace, jobDef.Name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
queryJ, err := job.AdaptFuncToEnsure(jobDef)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, feature := range features {
|
||||
switch feature {
|
||||
case Normal:
|
||||
destroyers = append(destroyers,
|
||||
operator.ResourceDestroyToZitadelDestroy(destroyCJ),
|
||||
)
|
||||
queriers = append(queriers,
|
||||
operator.EnsureFuncToQueryFunc(checkDBReady),
|
||||
operator.ResourceQueryToZitadelQuery(queryCJ),
|
||||
)
|
||||
case Instant:
|
||||
destroyers = append(destroyers,
|
||||
operator.ResourceDestroyToZitadelDestroy(destroyJ),
|
||||
)
|
||||
queriers = append(queriers,
|
||||
operator.EnsureFuncToQueryFunc(checkDBReady),
|
||||
operator.ResourceQueryToZitadelQuery(queryJ),
|
||||
operator.EnsureFuncToQueryFunc(getCleanupFunc(monitor, jobDef.Namespace, jobDef.Name)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return func(k8sClient kubernetes.ClientInt, queried map[string]interface{}) (operator.EnsureFunc, error) {
|
||||
return operator.QueriersToEnsureFunc(monitor, false, queriers, k8sClient, queried)
|
||||
},
|
||||
operator.DestroyersToDestroyFunc(monitor, destroyers),
|
||||
nil
|
||||
}
|
||||
|
||||
func GetJobName(backupName string) string {
|
||||
return cronJobNamePrefix + backupName
|
||||
}
|
307
operator/database/kinds/backups/bucket/backup/adapt_test.go
Normal file
307
operator/database/kinds/backups/bucket/backup/adapt_test.go
Normal file
@ -0,0 +1,307 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
kubernetesmock "github.com/caos/orbos/pkg/kubernetes/mock"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
macherrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBackup_AdaptInstantBackup1(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
features := []string{Instant}
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs"
|
||||
|
||||
databases := []string{"testDb"}
|
||||
bucketName := "testBucket"
|
||||
cron := "testCron"
|
||||
timestamp := "test"
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey", Operator: "testOp"}}
|
||||
backupName := "testName"
|
||||
version := "testVersion"
|
||||
secretKey := "testKey"
|
||||
secretName := "testSecretName"
|
||||
jobName := GetJobName(backupName)
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd2", "testOp2", "testVersion2"), "testKind2", "testVersion2"), "testComponent")
|
||||
nameLabels := labels.MustForName(componentLabels, jobName)
|
||||
|
||||
checkDBReady := func(k8sClient kubernetes.ClientInt) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
jobDef := getJob(
|
||||
namespace,
|
||||
nameLabels,
|
||||
getJobSpecDef(
|
||||
nodeselector,
|
||||
tolerations,
|
||||
secretName,
|
||||
secretKey,
|
||||
backupName,
|
||||
version,
|
||||
getBackupCommand(
|
||||
timestamp,
|
||||
databases,
|
||||
bucketName,
|
||||
backupName,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
client.EXPECT().ApplyJob(jobDef).Times(1).Return(nil)
|
||||
client.EXPECT().GetJob(jobDef.Namespace, jobDef.Name).Times(1).Return(nil, macherrs.NewNotFound(schema.GroupResource{"batch", "jobs"}, jobName))
|
||||
client.EXPECT().WaitUntilJobCompleted(jobDef.Namespace, jobDef.Name, timeout).Times(1).Return(nil)
|
||||
client.EXPECT().DeleteJob(jobDef.Namespace, jobDef.Name).Times(1).Return(nil)
|
||||
|
||||
query, _, err := AdaptFunc(
|
||||
monitor,
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
databases,
|
||||
checkDBReady,
|
||||
bucketName,
|
||||
cron,
|
||||
secretName,
|
||||
secretKey,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
features,
|
||||
version,
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
queried := map[string]interface{}{}
|
||||
ensure, err := query(client, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, ensure(client))
|
||||
}
|
||||
|
||||
func TestBackup_AdaptInstantBackup2(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
features := []string{Instant}
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs2"
|
||||
databases := []string{"testDb2"}
|
||||
bucketName := "testBucket2"
|
||||
cron := "testCron2"
|
||||
timestamp := "test2"
|
||||
nodeselector := map[string]string{"test2": "test2"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey2", Operator: "testOp2"}}
|
||||
backupName := "testName2"
|
||||
version := "testVersion2"
|
||||
secretKey := "testKey2"
|
||||
secretName := "testSecretName2"
|
||||
jobName := GetJobName(backupName)
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd2", "testOp2", "testVersion2"), "testKind2", "testVersion2"), "testComponent")
|
||||
nameLabels := labels.MustForName(componentLabels, jobName)
|
||||
|
||||
checkDBReady := func(k8sClient kubernetes.ClientInt) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
jobDef := getJob(
|
||||
namespace,
|
||||
nameLabels,
|
||||
getJobSpecDef(
|
||||
nodeselector,
|
||||
tolerations,
|
||||
secretName,
|
||||
secretKey,
|
||||
backupName,
|
||||
version,
|
||||
getBackupCommand(
|
||||
timestamp,
|
||||
databases,
|
||||
bucketName,
|
||||
backupName,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
client.EXPECT().ApplyJob(jobDef).Times(1).Return(nil)
|
||||
client.EXPECT().GetJob(jobDef.Namespace, jobDef.Name).Times(1).Return(nil, macherrs.NewNotFound(schema.GroupResource{"batch", "jobs"}, jobName))
|
||||
client.EXPECT().WaitUntilJobCompleted(jobDef.Namespace, jobDef.Name, timeout).Times(1).Return(nil)
|
||||
client.EXPECT().DeleteJob(jobDef.Namespace, jobDef.Name).Times(1).Return(nil)
|
||||
|
||||
query, _, err := AdaptFunc(
|
||||
monitor,
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
databases,
|
||||
checkDBReady,
|
||||
bucketName,
|
||||
cron,
|
||||
secretName,
|
||||
secretKey,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
features,
|
||||
version,
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
queried := map[string]interface{}{}
|
||||
ensure, err := query(client, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, ensure(client))
|
||||
}
|
||||
|
||||
func TestBackup_AdaptBackup1(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
features := []string{Normal}
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs"
|
||||
databases := []string{"testDb"}
|
||||
bucketName := "testBucket"
|
||||
cron := "testCron"
|
||||
timestamp := "test"
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey", Operator: "testOp"}}
|
||||
backupName := "testName"
|
||||
version := "testVersion"
|
||||
secretKey := "testKey"
|
||||
secretName := "testSecretName"
|
||||
jobName := GetJobName(backupName)
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd2", "testOp2", "testVersion2"), "testKind2", "testVersion2"), "testComponent")
|
||||
nameLabels := labels.MustForName(componentLabels, jobName)
|
||||
|
||||
checkDBReady := func(k8sClient kubernetes.ClientInt) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
jobDef := getCronJob(
|
||||
namespace,
|
||||
nameLabels,
|
||||
cron,
|
||||
getJobSpecDef(
|
||||
nodeselector,
|
||||
tolerations,
|
||||
secretName,
|
||||
secretKey,
|
||||
backupName,
|
||||
version,
|
||||
getBackupCommand(
|
||||
timestamp,
|
||||
databases,
|
||||
bucketName,
|
||||
backupName,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
client.EXPECT().ApplyCronJob(jobDef).Times(1).Return(nil)
|
||||
|
||||
query, _, err := AdaptFunc(
|
||||
monitor,
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
databases,
|
||||
checkDBReady,
|
||||
bucketName,
|
||||
cron,
|
||||
secretName,
|
||||
secretKey,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
features,
|
||||
version,
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
queried := map[string]interface{}{}
|
||||
ensure, err := query(client, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, ensure(client))
|
||||
}
|
||||
|
||||
func TestBackup_AdaptBackup2(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
features := []string{Normal}
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs2"
|
||||
databases := []string{"testDb2"}
|
||||
bucketName := "testBucket2"
|
||||
cron := "testCron2"
|
||||
timestamp := "test2"
|
||||
nodeselector := map[string]string{"test2": "test2"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey2", Operator: "testOp2"}}
|
||||
backupName := "testName2"
|
||||
version := "testVersion2"
|
||||
secretKey := "testKey2"
|
||||
secretName := "testSecretName2"
|
||||
jobName := GetJobName(backupName)
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd2", "testOp2", "testVersion2"), "testKind2", "testVersion2"), "testComponent")
|
||||
nameLabels := labels.MustForName(componentLabels, jobName)
|
||||
|
||||
checkDBReady := func(k8sClient kubernetes.ClientInt) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
jobDef := getCronJob(
|
||||
namespace,
|
||||
nameLabels,
|
||||
cron,
|
||||
getJobSpecDef(
|
||||
nodeselector,
|
||||
tolerations,
|
||||
secretName,
|
||||
secretKey,
|
||||
backupName,
|
||||
version,
|
||||
getBackupCommand(
|
||||
timestamp,
|
||||
databases,
|
||||
bucketName,
|
||||
backupName,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
client.EXPECT().ApplyCronJob(jobDef).Times(1).Return(nil)
|
||||
|
||||
query, _, err := AdaptFunc(
|
||||
monitor,
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
databases,
|
||||
checkDBReady,
|
||||
bucketName,
|
||||
cron,
|
||||
secretName,
|
||||
secretKey,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
features,
|
||||
version,
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
queried := map[string]interface{}{}
|
||||
ensure, err := query(client, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, ensure(client))
|
||||
}
|
25
operator/database/kinds/backups/bucket/backup/cleanup.go
Normal file
25
operator/database/kinds/backups/bucket/backup/cleanup.go
Normal file
@ -0,0 +1,25 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/zitadel/operator"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func getCleanupFunc(monitor mntr.Monitor, namespace string, name string) operator.EnsureFunc {
|
||||
return func(k8sClient kubernetes.ClientInt) error {
|
||||
monitor.Info("waiting for backup to be completed")
|
||||
if err := k8sClient.WaitUntilJobCompleted(namespace, name, timeout); err != nil {
|
||||
monitor.Error(errors.Wrap(err, "error while waiting for backup to be completed"))
|
||||
return err
|
||||
}
|
||||
monitor.Info("backup is completed, cleanup")
|
||||
if err := k8sClient.DeleteJob(namespace, name); err != nil {
|
||||
monitor.Error(errors.Wrap(err, "error while trying to cleanup backup"))
|
||||
return err
|
||||
}
|
||||
monitor.Info("restore backup is completed")
|
||||
return nil
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
kubernetesmock "github.com/caos/orbos/pkg/kubernetes/mock"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBackup_Cleanup1(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
monitor := mntr.Monitor{}
|
||||
name := "test"
|
||||
namespace := "testNs"
|
||||
|
||||
cleanupFunc := getCleanupFunc(monitor, namespace, name)
|
||||
client.EXPECT().WaitUntilJobCompleted(namespace, name, timeout).Times(1).Return(nil)
|
||||
client.EXPECT().DeleteJob(namespace, name).Times(1)
|
||||
assert.NoError(t, cleanupFunc(client))
|
||||
|
||||
client.EXPECT().WaitUntilJobCompleted(namespace, name, timeout).Times(1).Return(errors.New("fail"))
|
||||
assert.Error(t, cleanupFunc(client))
|
||||
}
|
||||
|
||||
func TestBackup_Cleanup2(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
monitor := mntr.Monitor{}
|
||||
name := "test2"
|
||||
namespace := "testNs2"
|
||||
|
||||
cleanupFunc := getCleanupFunc(monitor, namespace, name)
|
||||
client.EXPECT().WaitUntilJobCompleted(namespace, name, timeout).Times(1).Return(nil)
|
||||
client.EXPECT().DeleteJob(namespace, name).Times(1)
|
||||
assert.NoError(t, cleanupFunc(client))
|
||||
|
||||
client.EXPECT().WaitUntilJobCompleted(namespace, name, timeout).Times(1).Return(errors.New("fail"))
|
||||
assert.Error(t, cleanupFunc(client))
|
||||
}
|
33
operator/database/kinds/backups/bucket/backup/command.go
Normal file
33
operator/database/kinds/backups/bucket/backup/command.go
Normal file
@ -0,0 +1,33 @@
|
||||
package backup
|
||||
|
||||
import "strings"
|
||||
|
||||
func getBackupCommand(
|
||||
timestamp string,
|
||||
databases []string,
|
||||
bucketName string,
|
||||
backupName string,
|
||||
) string {
|
||||
|
||||
backupCommands := make([]string, 0)
|
||||
if timestamp != "" {
|
||||
backupCommands = append(backupCommands, "export "+backupNameEnv+"="+timestamp)
|
||||
} else {
|
||||
backupCommands = append(backupCommands, "export "+backupNameEnv+"=$(date +%Y-%m-%dT%H:%M:%SZ)")
|
||||
}
|
||||
|
||||
for _, database := range databases {
|
||||
backupCommands = append(backupCommands,
|
||||
strings.Join([]string{
|
||||
"/scripts/backup.sh",
|
||||
backupName,
|
||||
bucketName,
|
||||
database,
|
||||
backupPath,
|
||||
secretPath,
|
||||
certPath,
|
||||
"${" + backupNameEnv + "}",
|
||||
}, " "))
|
||||
}
|
||||
return strings.Join(backupCommands, " && ")
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBackup_Command1(t *testing.T) {
|
||||
timestamp := ""
|
||||
databases := []string{}
|
||||
bucketName := "test"
|
||||
backupName := "test"
|
||||
|
||||
cmd := getBackupCommand(timestamp, databases, bucketName, backupName)
|
||||
equals := "export " + backupNameEnv + "=$(date +%Y-%m-%dT%H:%M:%SZ)"
|
||||
assert.Equal(t, equals, cmd)
|
||||
}
|
||||
|
||||
func TestBackup_Command2(t *testing.T) {
|
||||
timestamp := "test"
|
||||
databases := []string{}
|
||||
bucketName := "test"
|
||||
backupName := "test"
|
||||
|
||||
cmd := getBackupCommand(timestamp, databases, bucketName, backupName)
|
||||
equals := "export " + backupNameEnv + "=test"
|
||||
assert.Equal(t, equals, cmd)
|
||||
}
|
||||
|
||||
func TestBackup_Command3(t *testing.T) {
|
||||
timestamp := ""
|
||||
databases := []string{"testDb"}
|
||||
bucketName := "testBucket"
|
||||
backupName := "testBackup"
|
||||
|
||||
cmd := getBackupCommand(timestamp, databases, bucketName, backupName)
|
||||
equals := "export " + backupNameEnv + "=$(date +%Y-%m-%dT%H:%M:%SZ) && /scripts/backup.sh testBackup testBucket testDb " + backupPath + " " + secretPath + " " + certPath + " ${" + backupNameEnv + "}"
|
||||
assert.Equal(t, equals, cmd)
|
||||
}
|
||||
|
||||
func TestBackup_Command4(t *testing.T) {
|
||||
timestamp := "test"
|
||||
databases := []string{"test1", "test2", "test3"}
|
||||
bucketName := "testBucket"
|
||||
backupName := "testBackup"
|
||||
|
||||
cmd := getBackupCommand(timestamp, databases, bucketName, backupName)
|
||||
equals := "export " + backupNameEnv + "=test && " +
|
||||
"/scripts/backup.sh testBackup testBucket test1 " + backupPath + " " + secretPath + " " + certPath + " ${" + backupNameEnv + "} && " +
|
||||
"/scripts/backup.sh testBackup testBucket test2 " + backupPath + " " + secretPath + " " + certPath + " ${" + backupNameEnv + "} && " +
|
||||
"/scripts/backup.sh testBackup testBucket test3 " + backupPath + " " + secretPath + " " + certPath + " ${" + backupNameEnv + "}"
|
||||
assert.Equal(t, equals, cmd)
|
||||
}
|
101
operator/database/kinds/backups/bucket/backup/job.go
Normal file
101
operator/database/kinds/backups/bucket/backup/job.go
Normal file
@ -0,0 +1,101 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/caos/zitadel/operator/helpers"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
"k8s.io/api/batch/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func getCronJob(
|
||||
namespace string,
|
||||
nameLabels *labels.Name,
|
||||
cron string,
|
||||
jobSpecDef batchv1.JobSpec,
|
||||
) *v1beta1.CronJob {
|
||||
return &v1beta1.CronJob{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: nameLabels.Name(),
|
||||
Namespace: namespace,
|
||||
Labels: labels.MustK8sMap(nameLabels),
|
||||
},
|
||||
Spec: v1beta1.CronJobSpec{
|
||||
Schedule: cron,
|
||||
ConcurrencyPolicy: v1beta1.ForbidConcurrent,
|
||||
JobTemplate: v1beta1.JobTemplateSpec{
|
||||
Spec: jobSpecDef,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getJob(
|
||||
namespace string,
|
||||
nameLabels *labels.Name,
|
||||
jobSpecDef batchv1.JobSpec,
|
||||
) *batchv1.Job {
|
||||
return &batchv1.Job{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: nameLabels.Name(),
|
||||
Namespace: namespace,
|
||||
Labels: labels.MustK8sMap(nameLabels),
|
||||
},
|
||||
Spec: jobSpecDef,
|
||||
}
|
||||
}
|
||||
|
||||
func getJobSpecDef(
|
||||
nodeselector map[string]string,
|
||||
tolerations []corev1.Toleration,
|
||||
secretName string,
|
||||
secretKey string,
|
||||
backupName string,
|
||||
version string,
|
||||
command string,
|
||||
) batchv1.JobSpec {
|
||||
return batchv1.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
NodeSelector: nodeselector,
|
||||
Tolerations: tolerations,
|
||||
Containers: []corev1.Container{{
|
||||
Name: backupName,
|
||||
Image: image + ":" + version,
|
||||
Command: []string{
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
command,
|
||||
},
|
||||
VolumeMounts: []corev1.VolumeMount{{
|
||||
Name: internalSecretName,
|
||||
MountPath: certPath,
|
||||
}, {
|
||||
Name: secretKey,
|
||||
SubPath: secretKey,
|
||||
MountPath: secretPath,
|
||||
}},
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
}},
|
||||
Volumes: []corev1.Volume{{
|
||||
Name: internalSecretName,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: rootSecretName,
|
||||
DefaultMode: helpers.PointerInt32(defaultMode),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: secretKey,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: secretName,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
123
operator/database/kinds/backups/bucket/backup/job_test.go
Normal file
123
operator/database/kinds/backups/bucket/backup/job_test.go
Normal file
@ -0,0 +1,123 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/operator/helpers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBackup_JobSpec1(t *testing.T) {
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey", Operator: "testOp"}}
|
||||
backupName := "testName"
|
||||
version := "testVersion"
|
||||
command := "test"
|
||||
secretKey := "testKey"
|
||||
secretName := "testSecretName"
|
||||
|
||||
equals := batchv1.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
NodeSelector: nodeselector,
|
||||
Tolerations: tolerations,
|
||||
Containers: []corev1.Container{{
|
||||
Name: backupName,
|
||||
Image: image + ":" + version,
|
||||
Command: []string{
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
command,
|
||||
},
|
||||
VolumeMounts: []corev1.VolumeMount{{
|
||||
Name: internalSecretName,
|
||||
MountPath: certPath,
|
||||
}, {
|
||||
Name: secretKey,
|
||||
SubPath: secretKey,
|
||||
MountPath: secretPath,
|
||||
}},
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
}},
|
||||
Volumes: []corev1.Volume{{
|
||||
Name: internalSecretName,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: rootSecretName,
|
||||
DefaultMode: helpers.PointerInt32(defaultMode),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: secretKey,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: secretName,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, equals, getJobSpecDef(nodeselector, tolerations, secretName, secretKey, backupName, version, command))
|
||||
}
|
||||
|
||||
func TestBackup_JobSpec2(t *testing.T) {
|
||||
nodeselector := map[string]string{"test2": "test2"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey2", Operator: "testOp2"}}
|
||||
backupName := "testName2"
|
||||
version := "testVersion2"
|
||||
command := "test2"
|
||||
secretKey := "testKey2"
|
||||
secretName := "testSecretName2"
|
||||
|
||||
equals := batchv1.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
NodeSelector: nodeselector,
|
||||
Tolerations: tolerations,
|
||||
Containers: []corev1.Container{{
|
||||
Name: backupName,
|
||||
Image: image + ":" + version,
|
||||
Command: []string{
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
command,
|
||||
},
|
||||
VolumeMounts: []corev1.VolumeMount{{
|
||||
Name: internalSecretName,
|
||||
MountPath: certPath,
|
||||
}, {
|
||||
Name: secretKey,
|
||||
SubPath: secretKey,
|
||||
MountPath: secretPath,
|
||||
}},
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
}},
|
||||
Volumes: []corev1.Volume{{
|
||||
Name: internalSecretName,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: rootSecretName,
|
||||
DefaultMode: helpers.PointerInt32(defaultMode),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: secretKey,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: secretName,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, equals, getJobSpecDef(nodeselector, tolerations, secretName, secretKey, backupName, version, command))
|
||||
}
|
86
operator/database/kinds/backups/bucket/clean/adapt.go
Normal file
86
operator/database/kinds/backups/bucket/clean/adapt.go
Normal file
@ -0,0 +1,86 @@
|
||||
package clean
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/operator"
|
||||
"time"
|
||||
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/orbos/pkg/kubernetes/resources/job"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
Instant = "clean"
|
||||
defaultMode = int32(256)
|
||||
certPath = "/cockroach/cockroach-certs"
|
||||
secretPath = "/secrets/sa.json"
|
||||
internalSecretName = "client-certs"
|
||||
image = "ghcr.io/caos/zitadel-crbackup"
|
||||
rootSecretName = "cockroachdb.client.root"
|
||||
jobPrefix = "backup-"
|
||||
jobSuffix = "-clean"
|
||||
timeout time.Duration = 60
|
||||
)
|
||||
|
||||
func AdaptFunc(
|
||||
monitor mntr.Monitor,
|
||||
backupName string,
|
||||
namespace string,
|
||||
componentLabels *labels.Component,
|
||||
databases []string,
|
||||
nodeselector map[string]string,
|
||||
tolerations []corev1.Toleration,
|
||||
checkDBReady operator.EnsureFunc,
|
||||
secretName string,
|
||||
secretKey string,
|
||||
version string,
|
||||
) (
|
||||
queryFunc operator.QueryFunc,
|
||||
destroyFunc operator.DestroyFunc,
|
||||
err error,
|
||||
) {
|
||||
|
||||
command := getCommand(databases)
|
||||
|
||||
jobDef := getJob(
|
||||
namespace,
|
||||
labels.MustForName(componentLabels, GetJobName(backupName)),
|
||||
nodeselector,
|
||||
tolerations,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
command)
|
||||
|
||||
destroyJ, err := job.AdaptFuncToDestroy(jobDef.Namespace, jobDef.Name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
destroyers := []operator.DestroyFunc{
|
||||
operator.ResourceDestroyToZitadelDestroy(destroyJ),
|
||||
}
|
||||
|
||||
queryJ, err := job.AdaptFuncToEnsure(jobDef)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
queriers := []operator.QueryFunc{
|
||||
operator.EnsureFuncToQueryFunc(checkDBReady),
|
||||
operator.ResourceQueryToZitadelQuery(queryJ),
|
||||
operator.EnsureFuncToQueryFunc(getCleanupFunc(monitor, jobDef.Namespace, jobDef.Name)),
|
||||
}
|
||||
|
||||
return func(k8sClient kubernetes.ClientInt, queried map[string]interface{}) (operator.EnsureFunc, error) {
|
||||
return operator.QueriersToEnsureFunc(monitor, false, queriers, k8sClient, queried)
|
||||
},
|
||||
operator.DestroyersToDestroyFunc(monitor, destroyers),
|
||||
nil
|
||||
}
|
||||
|
||||
func GetJobName(backupName string) string {
|
||||
return jobPrefix + backupName + jobSuffix
|
||||
}
|
134
operator/database/kinds/backups/bucket/clean/adapt_test.go
Normal file
134
operator/database/kinds/backups/bucket/clean/adapt_test.go
Normal file
@ -0,0 +1,134 @@
|
||||
package clean
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
kubernetesmock "github.com/caos/orbos/pkg/kubernetes/mock"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
macherrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBackup_Adapt1(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs"
|
||||
databases := []string{"testDb"}
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey", Operator: "testOp"}}
|
||||
backupName := "testName"
|
||||
version := "testVersion"
|
||||
secretKey := "testKey"
|
||||
secretName := "testSecretName"
|
||||
jobName := GetJobName(backupName)
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "testKind", "testVersion"), "testComponent")
|
||||
nameLabels := labels.MustForName(componentLabels, jobName)
|
||||
|
||||
checkDBReady := func(k8sClient kubernetes.ClientInt) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
jobDef := getJob(
|
||||
namespace,
|
||||
nameLabels,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
getCommand(
|
||||
databases,
|
||||
),
|
||||
)
|
||||
|
||||
client.EXPECT().ApplyJob(jobDef).Times(1).Return(nil)
|
||||
client.EXPECT().GetJob(jobDef.Namespace, jobDef.Name).Times(1).Return(nil, macherrs.NewNotFound(schema.GroupResource{"batch", "jobs"}, jobName))
|
||||
client.EXPECT().WaitUntilJobCompleted(jobDef.Namespace, jobDef.Name, timeout).Times(1).Return(nil)
|
||||
client.EXPECT().DeleteJob(jobDef.Namespace, jobDef.Name).Times(1).Return(nil)
|
||||
|
||||
query, _, err := AdaptFunc(
|
||||
monitor,
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
databases,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
checkDBReady,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
queried := map[string]interface{}{}
|
||||
ensure, err := query(client, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, ensure(client))
|
||||
}
|
||||
|
||||
func TestBackup_Adapt2(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs2"
|
||||
databases := []string{"testDb1", "testDb2"}
|
||||
nodeselector := map[string]string{"test2": "test2"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey2", Operator: "testOp2"}}
|
||||
backupName := "testName2"
|
||||
version := "testVersion2"
|
||||
secretKey := "testKey2"
|
||||
secretName := "testSecretName2"
|
||||
jobName := GetJobName(backupName)
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd2", "testOp2", "testVersion2"), "testKind2", "testVersion2"), "testComponent2")
|
||||
nameLabels := labels.MustForName(componentLabels, jobName)
|
||||
|
||||
checkDBReady := func(k8sClient kubernetes.ClientInt) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
jobDef := getJob(
|
||||
namespace,
|
||||
nameLabels,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
getCommand(
|
||||
databases,
|
||||
),
|
||||
)
|
||||
|
||||
client.EXPECT().ApplyJob(jobDef).Times(1).Return(nil)
|
||||
client.EXPECT().GetJob(jobDef.Namespace, jobDef.Name).Times(1).Return(nil, macherrs.NewNotFound(schema.GroupResource{"batch", "jobs"}, jobName))
|
||||
client.EXPECT().WaitUntilJobCompleted(jobDef.Namespace, jobDef.Name, timeout).Times(1).Return(nil)
|
||||
client.EXPECT().DeleteJob(jobDef.Namespace, jobDef.Name).Times(1).Return(nil)
|
||||
|
||||
query, _, err := AdaptFunc(
|
||||
monitor,
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
databases,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
checkDBReady,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
queried := map[string]interface{}{}
|
||||
ensure, err := query(client, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, ensure(client))
|
||||
}
|
29
operator/database/kinds/backups/bucket/clean/cleanup.go
Normal file
29
operator/database/kinds/backups/bucket/clean/cleanup.go
Normal file
@ -0,0 +1,29 @@
|
||||
package clean
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/zitadel/operator"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func getCleanupFunc(
|
||||
monitor mntr.Monitor,
|
||||
namespace string,
|
||||
jobName string,
|
||||
) operator.EnsureFunc {
|
||||
return func(k8sClient kubernetes.ClientInt) error {
|
||||
monitor.Info("waiting for clean to be completed")
|
||||
if err := k8sClient.WaitUntilJobCompleted(namespace, jobName, 60); err != nil {
|
||||
monitor.Error(errors.Wrap(err, "error while waiting for clean to be completed"))
|
||||
return err
|
||||
}
|
||||
monitor.Info("clean is completed, cleanup")
|
||||
if err := k8sClient.DeleteJob(namespace, jobName); err != nil {
|
||||
monitor.Error(errors.Wrap(err, "error while trying to cleanup clean"))
|
||||
return err
|
||||
}
|
||||
monitor.Info("clean cleanup is completed")
|
||||
return nil
|
||||
}
|
||||
}
|
40
operator/database/kinds/backups/bucket/clean/cleanup_test.go
Normal file
40
operator/database/kinds/backups/bucket/clean/cleanup_test.go
Normal file
@ -0,0 +1,40 @@
|
||||
package clean
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
kubernetesmock "github.com/caos/orbos/pkg/kubernetes/mock"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBackup_Cleanup1(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
monitor := mntr.Monitor{}
|
||||
name := "test"
|
||||
namespace := "testNs"
|
||||
|
||||
cleanupFunc := getCleanupFunc(monitor, namespace, name)
|
||||
client.EXPECT().WaitUntilJobCompleted(namespace, name, timeout).Times(1).Return(nil)
|
||||
client.EXPECT().DeleteJob(namespace, name).Times(1)
|
||||
assert.NoError(t, cleanupFunc(client))
|
||||
|
||||
client.EXPECT().WaitUntilJobCompleted(namespace, name, timeout).Times(1).Return(errors.New("fail"))
|
||||
assert.Error(t, cleanupFunc(client))
|
||||
}
|
||||
|
||||
func TestBackup_Cleanup2(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
monitor := mntr.Monitor{}
|
||||
name := "test2"
|
||||
namespace := "testNs2"
|
||||
|
||||
cleanupFunc := getCleanupFunc(monitor, namespace, name)
|
||||
client.EXPECT().WaitUntilJobCompleted(namespace, name, timeout).Times(1).Return(nil)
|
||||
client.EXPECT().DeleteJob(namespace, name).Times(1)
|
||||
assert.NoError(t, cleanupFunc(client))
|
||||
|
||||
client.EXPECT().WaitUntilJobCompleted(namespace, name, timeout).Times(1).Return(errors.New("fail"))
|
||||
assert.Error(t, cleanupFunc(client))
|
||||
}
|
32
operator/database/kinds/backups/bucket/clean/command.go
Normal file
32
operator/database/kinds/backups/bucket/clean/command.go
Normal file
@ -0,0 +1,32 @@
|
||||
package clean
|
||||
|
||||
import "strings"
|
||||
|
||||
func getCommand(
|
||||
databases []string,
|
||||
) string {
|
||||
backupCommands := make([]string, 0)
|
||||
for _, database := range databases {
|
||||
backupCommands = append(backupCommands,
|
||||
strings.Join([]string{
|
||||
"/scripts/clean-db.sh",
|
||||
certPath,
|
||||
database,
|
||||
}, " "))
|
||||
}
|
||||
for _, database := range databases {
|
||||
backupCommands = append(backupCommands,
|
||||
strings.Join([]string{
|
||||
"/scripts/clean-user.sh",
|
||||
certPath,
|
||||
database,
|
||||
}, " "))
|
||||
}
|
||||
backupCommands = append(backupCommands,
|
||||
strings.Join([]string{
|
||||
"/scripts/clean-migration.sh",
|
||||
certPath,
|
||||
}, " "))
|
||||
|
||||
return strings.Join(backupCommands, " && ")
|
||||
}
|
35
operator/database/kinds/backups/bucket/clean/command_test.go
Normal file
35
operator/database/kinds/backups/bucket/clean/command_test.go
Normal file
@ -0,0 +1,35 @@
|
||||
package clean
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClean_Command1(t *testing.T) {
|
||||
databases := []string{}
|
||||
|
||||
cmd := getCommand(databases)
|
||||
equals := "/scripts/clean-migration.sh " + certPath
|
||||
|
||||
assert.Equal(t, equals, cmd)
|
||||
}
|
||||
|
||||
func TestClean_Command2(t *testing.T) {
|
||||
databases := []string{"test"}
|
||||
|
||||
cmd := getCommand(databases)
|
||||
equals := "/scripts/clean-db.sh " + certPath + " test && /scripts/clean-user.sh " + certPath + " test && /scripts/clean-migration.sh " + certPath
|
||||
|
||||
assert.Equal(t, equals, cmd)
|
||||
}
|
||||
|
||||
func TestClean_Command3(t *testing.T) {
|
||||
databases := []string{"test1", "test2", "test3"}
|
||||
|
||||
cmd := getCommand(databases)
|
||||
equals := "/scripts/clean-db.sh " + certPath + " test1 && /scripts/clean-db.sh " + certPath + " test2 && /scripts/clean-db.sh " + certPath + " test3 && " +
|
||||
"/scripts/clean-user.sh " + certPath + " test1 && /scripts/clean-user.sh " + certPath + " test2 && /scripts/clean-user.sh " + certPath + " test3 && " +
|
||||
"/scripts/clean-migration.sh " + certPath
|
||||
|
||||
assert.Equal(t, equals, cmd)
|
||||
}
|
73
operator/database/kinds/backups/bucket/clean/job.go
Normal file
73
operator/database/kinds/backups/bucket/clean/job.go
Normal file
@ -0,0 +1,73 @@
|
||||
package clean
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/caos/zitadel/operator/helpers"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func getJob(
|
||||
namespace string,
|
||||
nameLabels *labels.Name,
|
||||
nodeselector map[string]string,
|
||||
tolerations []corev1.Toleration,
|
||||
secretName string,
|
||||
secretKey string,
|
||||
version string,
|
||||
command string,
|
||||
) *batchv1.Job {
|
||||
|
||||
return &batchv1.Job{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: nameLabels.Name(),
|
||||
Namespace: namespace,
|
||||
Labels: labels.MustK8sMap(nameLabels),
|
||||
},
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
NodeSelector: nodeselector,
|
||||
Tolerations: tolerations,
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
Containers: []corev1.Container{{
|
||||
Name: nameLabels.Name(),
|
||||
Image: image + ":" + version,
|
||||
Command: []string{
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
command,
|
||||
},
|
||||
VolumeMounts: []corev1.VolumeMount{{
|
||||
Name: internalSecretName,
|
||||
MountPath: certPath,
|
||||
}, {
|
||||
Name: secretKey,
|
||||
SubPath: secretKey,
|
||||
MountPath: secretPath,
|
||||
}},
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
}},
|
||||
Volumes: []corev1.Volume{{
|
||||
Name: internalSecretName,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: rootSecretName,
|
||||
DefaultMode: helpers.PointerInt32(defaultMode),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: secretKey,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: secretName,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
164
operator/database/kinds/backups/bucket/clean/job_test.go
Normal file
164
operator/database/kinds/backups/bucket/clean/job_test.go
Normal file
@ -0,0 +1,164 @@
|
||||
package clean
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/caos/zitadel/operator/helpers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBackup_Job1(t *testing.T) {
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey", Operator: "testOp"}}
|
||||
version := "testVersion"
|
||||
command := "test"
|
||||
secretKey := "testKey"
|
||||
secretName := "testSecretName"
|
||||
jobName := "testJob"
|
||||
namespace := "testNs"
|
||||
|
||||
k8sLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "testComponent",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": jobName,
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"app.kubernetes.io/version": "testOpVersion",
|
||||
"caos.ch/apiversion": "testVersion",
|
||||
"caos.ch/kind": "testKind"}
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testOpVersion"), "testKind", "testVersion"), "testComponent")
|
||||
nameLabels := labels.MustForName(componentLabels, jobName)
|
||||
|
||||
equals :=
|
||||
&batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: jobName,
|
||||
Namespace: namespace,
|
||||
Labels: k8sLabels,
|
||||
},
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
NodeSelector: nodeselector,
|
||||
Tolerations: tolerations,
|
||||
Containers: []corev1.Container{{
|
||||
Name: jobName,
|
||||
Image: image + ":" + version,
|
||||
Command: []string{
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
command,
|
||||
},
|
||||
VolumeMounts: []corev1.VolumeMount{{
|
||||
Name: internalSecretName,
|
||||
MountPath: certPath,
|
||||
}, {
|
||||
Name: secretKey,
|
||||
SubPath: secretKey,
|
||||
MountPath: secretPath,
|
||||
}},
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
}},
|
||||
Volumes: []corev1.Volume{{
|
||||
Name: internalSecretName,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: rootSecretName,
|
||||
DefaultMode: helpers.PointerInt32(defaultMode),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: secretKey,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: secretName,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, equals, getJob(namespace, nameLabels, nodeselector, tolerations, secretName, secretKey, version, command))
|
||||
}
|
||||
|
||||
func TestBackup_Job2(t *testing.T) {
|
||||
nodeselector := map[string]string{"test2": "test2"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey2", Operator: "testOp2"}}
|
||||
version := "testVersion2"
|
||||
command := "test2"
|
||||
secretKey := "testKey2"
|
||||
secretName := "testSecretName2"
|
||||
jobName := "testJob2"
|
||||
namespace := "testNs2"
|
||||
k8sLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "testComponent2",
|
||||
"app.kubernetes.io/managed-by": "testOp2",
|
||||
"app.kubernetes.io/name": jobName,
|
||||
"app.kubernetes.io/part-of": "testProd2",
|
||||
"app.kubernetes.io/version": "testOpVersion2",
|
||||
"caos.ch/apiversion": "testVersion2",
|
||||
"caos.ch/kind": "testKind2"}
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd2", "testOp2", "testOpVersion2"), "testKind2", "testVersion2"), "testComponent2")
|
||||
nameLabels := labels.MustForName(componentLabels, jobName)
|
||||
|
||||
equals :=
|
||||
&batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: jobName,
|
||||
Namespace: namespace,
|
||||
Labels: k8sLabels,
|
||||
},
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
NodeSelector: nodeselector,
|
||||
Tolerations: tolerations,
|
||||
Containers: []corev1.Container{{
|
||||
Name: jobName,
|
||||
Image: image + ":" + version,
|
||||
Command: []string{
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
command,
|
||||
},
|
||||
VolumeMounts: []corev1.VolumeMount{{
|
||||
Name: internalSecretName,
|
||||
MountPath: certPath,
|
||||
}, {
|
||||
Name: secretKey,
|
||||
SubPath: secretKey,
|
||||
MountPath: secretPath,
|
||||
}},
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
}},
|
||||
Volumes: []corev1.Volume{{
|
||||
Name: internalSecretName,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: rootSecretName,
|
||||
DefaultMode: helpers.PointerInt32(defaultMode),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: secretKey,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: secretName,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, equals, getJob(namespace, nameLabels, nodeselector, tolerations, secretName, secretKey, version, command))
|
||||
}
|
42
operator/database/kinds/backups/bucket/desired.go
Normal file
42
operator/database/kinds/backups/bucket/desired.go
Normal file
@ -0,0 +1,42 @@
|
||||
package bucket
|
||||
|
||||
import (
|
||||
secret2 "github.com/caos/orbos/pkg/secret"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type DesiredV0 struct {
|
||||
Common *tree.Common `yaml:",inline"`
|
||||
Spec *Spec
|
||||
}
|
||||
|
||||
type Spec struct {
|
||||
Verbose bool
|
||||
Cron string `yaml:"cron,omitempty"`
|
||||
Bucket string `yaml:"bucket,omitempty"`
|
||||
ServiceAccountJSON *secret2.Secret `yaml:"serviceAccountJSON,omitempty"`
|
||||
}
|
||||
|
||||
func (s *Spec) IsZero() bool {
|
||||
if (s.ServiceAccountJSON == nil || s.ServiceAccountJSON.IsZero()) &&
|
||||
!s.Verbose &&
|
||||
s.Cron == "" &&
|
||||
s.Bucket == "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ParseDesiredV0(desiredTree *tree.Tree) (*DesiredV0, error) {
|
||||
desiredKind := &DesiredV0{
|
||||
Common: desiredTree.Common,
|
||||
Spec: &Spec{},
|
||||
}
|
||||
|
||||
if err := desiredTree.Original.Decode(desiredKind); err != nil {
|
||||
return nil, errors.Wrap(err, "parsing desired state failed")
|
||||
}
|
||||
|
||||
return desiredKind, nil
|
||||
}
|
129
operator/database/kinds/backups/bucket/desired_test.go
Normal file
129
operator/database/kinds/backups/bucket/desired_test.go
Normal file
@ -0,0 +1,129 @@
|
||||
package bucket
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/pkg/secret"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
masterkey = "testMk"
|
||||
cron = "testCron"
|
||||
bucketName = "testBucket"
|
||||
saJson = "testSa"
|
||||
yamlFile = `kind: databases.caos.ch/BucketBackup
|
||||
version: v0
|
||||
spec:
|
||||
verbose: true
|
||||
cron: testCron
|
||||
bucket: testBucket
|
||||
serviceAccountJSON:
|
||||
encryption: AES256
|
||||
encoding: Base64
|
||||
value: luyAqtopzwLcaIhJj7KhWmbUsA7cQg==
|
||||
`
|
||||
|
||||
yamlFileWithoutSecret = `kind: databases.caos.ch/BucketBackup
|
||||
version: v0
|
||||
spec:
|
||||
verbose: true
|
||||
cron: testCron
|
||||
bucket: testBucket
|
||||
`
|
||||
yamlEmpty = `kind: databases.caos.ch/BucketBackup
|
||||
version: v0`
|
||||
)
|
||||
|
||||
var (
|
||||
desired = DesiredV0{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/BucketBackup",
|
||||
Version: "v0",
|
||||
},
|
||||
Spec: &Spec{
|
||||
Verbose: true,
|
||||
Cron: cron,
|
||||
Bucket: bucketName,
|
||||
ServiceAccountJSON: &secret.Secret{
|
||||
Value: saJson,
|
||||
Encryption: "AES256",
|
||||
Encoding: "Base64",
|
||||
},
|
||||
},
|
||||
}
|
||||
desiredWithoutSecret = DesiredV0{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/BucketBackup",
|
||||
Version: "v0",
|
||||
},
|
||||
Spec: &Spec{
|
||||
Verbose: true,
|
||||
Cron: cron,
|
||||
Bucket: bucketName,
|
||||
},
|
||||
}
|
||||
desiredEmpty = DesiredV0{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/BucketBackup",
|
||||
Version: "v0",
|
||||
},
|
||||
Spec: &Spec{
|
||||
Verbose: false,
|
||||
Cron: "",
|
||||
Bucket: "",
|
||||
ServiceAccountJSON: &secret.Secret{
|
||||
Value: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
desiredNil = DesiredV0{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/BucketBackup",
|
||||
Version: "v0",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func marshalYaml(t *testing.T, masterkey string, struc *DesiredV0) []byte {
|
||||
secret.Masterkey = masterkey
|
||||
data, err := yaml.Marshal(struc)
|
||||
assert.NoError(t, err)
|
||||
return data
|
||||
}
|
||||
|
||||
func unmarshalYaml(t *testing.T, masterkey string, yamlFile []byte) *tree.Tree {
|
||||
secret.Masterkey = masterkey
|
||||
desiredTree := &tree.Tree{}
|
||||
assert.NoError(t, yaml.Unmarshal(yamlFile, desiredTree))
|
||||
return desiredTree
|
||||
}
|
||||
|
||||
func getDesiredTree(t *testing.T, masterkey string, desired *DesiredV0) *tree.Tree {
|
||||
return unmarshalYaml(t, masterkey, marshalYaml(t, masterkey, desired))
|
||||
}
|
||||
|
||||
func TestBucket_DesiredParse(t *testing.T) {
|
||||
assert.Equal(t, yamlFileWithoutSecret, string(marshalYaml(t, masterkey, &desiredWithoutSecret)))
|
||||
|
||||
desiredTree := unmarshalYaml(t, masterkey, []byte(yamlFile))
|
||||
desiredKind, err := ParseDesiredV0(desiredTree)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &desired, desiredKind)
|
||||
}
|
||||
|
||||
func TestBucket_DesiredNotZero(t *testing.T) {
|
||||
desiredTree := unmarshalYaml(t, masterkey, []byte(yamlFile))
|
||||
desiredKind, err := ParseDesiredV0(desiredTree)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, desiredKind.Spec.IsZero())
|
||||
}
|
||||
|
||||
func TestBucket_DesiredZero(t *testing.T) {
|
||||
desiredTree := unmarshalYaml(t, masterkey, []byte(yamlEmpty))
|
||||
desiredKind, err := ParseDesiredV0(desiredTree)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, desiredKind.Spec.IsZero())
|
||||
}
|
63
operator/database/kinds/backups/bucket/list.go
Normal file
63
operator/database/kinds/backups/bucket/list.go
Normal file
@ -0,0 +1,63 @@
|
||||
package bucket
|
||||
|
||||
import (
|
||||
"cloud.google.com/go/storage"
|
||||
"context"
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/core"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/api/iterator"
|
||||
"google.golang.org/api/option"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func BackupList() core.BackupListFunc {
|
||||
return func(monitor mntr.Monitor, name string, desired *tree.Tree) ([]string, error) {
|
||||
desiredKind, err := ParseDesiredV0(desired)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "parsing desired state failed")
|
||||
}
|
||||
desired.Parsed = desiredKind
|
||||
|
||||
if !monitor.IsVerbose() && desiredKind.Spec.Verbose {
|
||||
monitor.Verbose()
|
||||
}
|
||||
|
||||
return listFilesWithFilter(desiredKind.Spec.ServiceAccountJSON.Value, desiredKind.Spec.Bucket, name)
|
||||
}
|
||||
}
|
||||
|
||||
func listFilesWithFilter(serviceAccountJSON string, bucketName, name string) ([]string, error) {
|
||||
ctx := context.Background()
|
||||
client, err := storage.NewClient(ctx, option.WithCredentialsJSON([]byte(serviceAccountJSON)))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bkt := client.Bucket(bucketName)
|
||||
|
||||
names := make([]string, 0)
|
||||
it := bkt.Objects(ctx, &storage.Query{Prefix: name + "/"})
|
||||
for {
|
||||
attrs, err := it.Next()
|
||||
if err == iterator.Done {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parts := strings.Split(attrs.Name, "/")
|
||||
found := false
|
||||
for _, name := range names {
|
||||
if name == parts[1] {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
names = append(names, parts[1])
|
||||
}
|
||||
}
|
||||
|
||||
return names, nil
|
||||
}
|
97
operator/database/kinds/backups/bucket/mock.go
Normal file
97
operator/database/kinds/backups/bucket/mock.go
Normal file
@ -0,0 +1,97 @@
|
||||
package bucket
|
||||
|
||||
import (
|
||||
kubernetesmock "github.com/caos/orbos/pkg/kubernetes/mock"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket/backup"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket/clean"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket/restore"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/core"
|
||||
"github.com/golang/mock/gomock"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
macherrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func SetQueriedForDatabases(databases []string) map[string]interface{} {
|
||||
queried := map[string]interface{}{}
|
||||
core.SetQueriedForDatabaseDBList(queried, databases)
|
||||
|
||||
return queried
|
||||
}
|
||||
|
||||
func SetInstantBackup(
|
||||
k8sClient *kubernetesmock.MockClientInt,
|
||||
namespace string,
|
||||
backupName string,
|
||||
labels map[string]string,
|
||||
saJson string,
|
||||
) {
|
||||
k8sClient.EXPECT().ApplySecret(&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: secretName,
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
StringData: map[string]string{secretKey: saJson},
|
||||
Type: "Opaque",
|
||||
}).Times(1).Return(nil)
|
||||
|
||||
k8sClient.EXPECT().ApplyJob(gomock.Any()).Times(1).Return(nil)
|
||||
k8sClient.EXPECT().GetJob(namespace, backup.GetJobName(backupName)).Times(1).Return(nil, macherrs.NewNotFound(schema.GroupResource{"batch", "jobs"}, backup.GetJobName(backupName)))
|
||||
k8sClient.EXPECT().WaitUntilJobCompleted(namespace, backup.GetJobName(backupName), gomock.Any()).Times(1).Return(nil)
|
||||
k8sClient.EXPECT().DeleteJob(namespace, backup.GetJobName(backupName)).Times(1).Return(nil)
|
||||
}
|
||||
|
||||
func SetBackup(
|
||||
k8sClient *kubernetesmock.MockClientInt,
|
||||
namespace string,
|
||||
labels map[string]string,
|
||||
saJson string,
|
||||
) {
|
||||
k8sClient.EXPECT().ApplySecret(&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: secretName,
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
StringData: map[string]string{secretKey: saJson},
|
||||
Type: "Opaque",
|
||||
}).Times(1).Return(nil)
|
||||
k8sClient.EXPECT().ApplyCronJob(gomock.Any()).Times(1).Return(nil)
|
||||
}
|
||||
|
||||
func SetClean(
|
||||
k8sClient *kubernetesmock.MockClientInt,
|
||||
namespace string,
|
||||
backupName string,
|
||||
) {
|
||||
|
||||
k8sClient.EXPECT().ApplyJob(gomock.Any()).Times(1).Return(nil)
|
||||
k8sClient.EXPECT().GetJob(namespace, clean.GetJobName(backupName)).Times(1).Return(nil, macherrs.NewNotFound(schema.GroupResource{"batch", "jobs"}, clean.GetJobName(backupName)))
|
||||
k8sClient.EXPECT().WaitUntilJobCompleted(namespace, clean.GetJobName(backupName), gomock.Any()).Times(1).Return(nil)
|
||||
k8sClient.EXPECT().DeleteJob(namespace, clean.GetJobName(backupName)).Times(1).Return(nil)
|
||||
}
|
||||
|
||||
func SetRestore(
|
||||
k8sClient *kubernetesmock.MockClientInt,
|
||||
namespace string,
|
||||
backupName string,
|
||||
labels map[string]string,
|
||||
saJson string,
|
||||
) {
|
||||
k8sClient.EXPECT().ApplySecret(&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: secretName,
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
StringData: map[string]string{secretKey: saJson},
|
||||
Type: "Opaque",
|
||||
}).Times(1).Return(nil)
|
||||
|
||||
k8sClient.EXPECT().ApplyJob(gomock.Any()).Times(1).Return(nil)
|
||||
k8sClient.EXPECT().GetJob(namespace, restore.GetJobName(backupName)).Times(1).Return(nil, macherrs.NewNotFound(schema.GroupResource{"batch", "jobs"}, restore.GetJobName(backupName)))
|
||||
k8sClient.EXPECT().WaitUntilJobCompleted(namespace, restore.GetJobName(backupName), gomock.Any()).Times(1).Return(nil)
|
||||
k8sClient.EXPECT().DeleteJob(namespace, restore.GetJobName(backupName)).Times(1).Return(nil)
|
||||
}
|
95
operator/database/kinds/backups/bucket/restore/adapt.go
Normal file
95
operator/database/kinds/backups/bucket/restore/adapt.go
Normal file
@ -0,0 +1,95 @@
|
||||
package restore
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/operator"
|
||||
"time"
|
||||
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/orbos/pkg/kubernetes/resources/job"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
Instant = "restore"
|
||||
defaultMode = int32(256)
|
||||
certPath = "/cockroach/cockroach-certs"
|
||||
secretPath = "/secrets/sa.json"
|
||||
jobPrefix = "backup-"
|
||||
jobSuffix = "-restore"
|
||||
image = "ghcr.io/caos/zitadel-crbackup"
|
||||
internalSecretName = "client-certs"
|
||||
rootSecretName = "cockroachdb.client.root"
|
||||
timeout time.Duration = 60
|
||||
)
|
||||
|
||||
func AdaptFunc(
|
||||
monitor mntr.Monitor,
|
||||
backupName string,
|
||||
namespace string,
|
||||
componentLabels *labels.Component,
|
||||
databases []string,
|
||||
bucketName string,
|
||||
timestamp string,
|
||||
nodeselector map[string]string,
|
||||
tolerations []corev1.Toleration,
|
||||
checkDBReady operator.EnsureFunc,
|
||||
secretName string,
|
||||
secretKey string,
|
||||
version string,
|
||||
) (
|
||||
queryFunc operator.QueryFunc,
|
||||
destroyFunc operator.DestroyFunc,
|
||||
err error,
|
||||
) {
|
||||
|
||||
jobName := jobPrefix + backupName + jobSuffix
|
||||
command := getCommand(
|
||||
timestamp,
|
||||
databases,
|
||||
bucketName,
|
||||
backupName,
|
||||
)
|
||||
|
||||
jobdef := getJob(
|
||||
namespace,
|
||||
labels.MustForName(componentLabels, GetJobName(backupName)),
|
||||
nodeselector,
|
||||
tolerations,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
command)
|
||||
|
||||
destroyJ, err := job.AdaptFuncToDestroy(jobName, namespace)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
destroyers := []operator.DestroyFunc{
|
||||
operator.ResourceDestroyToZitadelDestroy(destroyJ),
|
||||
}
|
||||
|
||||
queryJ, err := job.AdaptFuncToEnsure(jobdef)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
queriers := []operator.QueryFunc{
|
||||
operator.EnsureFuncToQueryFunc(checkDBReady),
|
||||
operator.ResourceQueryToZitadelQuery(queryJ),
|
||||
operator.EnsureFuncToQueryFunc(getCleanupFunc(monitor, jobdef.Namespace, jobdef.Name)),
|
||||
}
|
||||
|
||||
return func(k8sClient kubernetes.ClientInt, queried map[string]interface{}) (operator.EnsureFunc, error) {
|
||||
return operator.QueriersToEnsureFunc(monitor, false, queriers, k8sClient, queried)
|
||||
},
|
||||
operator.DestroyersToDestroyFunc(monitor, destroyers),
|
||||
|
||||
nil
|
||||
}
|
||||
|
||||
func GetJobName(backupName string) string {
|
||||
return jobPrefix + backupName + jobSuffix
|
||||
}
|
148
operator/database/kinds/backups/bucket/restore/adapt_test.go
Normal file
148
operator/database/kinds/backups/bucket/restore/adapt_test.go
Normal file
@ -0,0 +1,148 @@
|
||||
package restore
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
kubernetesmock "github.com/caos/orbos/pkg/kubernetes/mock"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
macherrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBackup_Adapt1(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs"
|
||||
databases := []string{"testDb"}
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey", Operator: "testOp"}}
|
||||
timestamp := "testTs"
|
||||
backupName := "testName2"
|
||||
bucketName := "testBucket2"
|
||||
version := "testVersion"
|
||||
secretKey := "testKey"
|
||||
secretName := "testSecretName"
|
||||
jobName := GetJobName(backupName)
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "testKind", "testVersion"), "testComponent")
|
||||
nameLabels := labels.MustForName(componentLabels, jobName)
|
||||
|
||||
checkDBReady := func(k8sClient kubernetes.ClientInt) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
jobDef := getJob(
|
||||
namespace,
|
||||
nameLabels,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
getCommand(
|
||||
timestamp,
|
||||
databases,
|
||||
bucketName,
|
||||
backupName,
|
||||
),
|
||||
)
|
||||
|
||||
client.EXPECT().ApplyJob(jobDef).Times(1).Return(nil)
|
||||
client.EXPECT().GetJob(jobDef.Namespace, jobDef.Name).Times(1).Return(nil, macherrs.NewNotFound(schema.GroupResource{"batch", "jobs"}, jobName))
|
||||
client.EXPECT().WaitUntilJobCompleted(jobDef.Namespace, jobDef.Name, timeout).Times(1).Return(nil)
|
||||
client.EXPECT().DeleteJob(jobDef.Namespace, jobDef.Name).Times(1).Return(nil)
|
||||
|
||||
query, _, err := AdaptFunc(
|
||||
monitor,
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
databases,
|
||||
bucketName,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
checkDBReady,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
queried := map[string]interface{}{}
|
||||
ensure, err := query(client, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, ensure(client))
|
||||
}
|
||||
|
||||
func TestBackup_Adapt2(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs2"
|
||||
databases := []string{"testDb1", "testDb2"}
|
||||
nodeselector := map[string]string{"test2": "test2"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey2", Operator: "testOp2"}}
|
||||
timestamp := "testTs"
|
||||
backupName := "testName2"
|
||||
bucketName := "testBucket2"
|
||||
version := "testVersion2"
|
||||
secretKey := "testKey2"
|
||||
secretName := "testSecretName2"
|
||||
jobName := GetJobName(backupName)
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd2", "testOp2", "testVersion2"), "testKind2", "testVersion2"), "testComponent2")
|
||||
nameLabels := labels.MustForName(componentLabels, jobName)
|
||||
|
||||
checkDBReady := func(k8sClient kubernetes.ClientInt) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
jobDef := getJob(
|
||||
namespace,
|
||||
nameLabels,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
getCommand(
|
||||
timestamp,
|
||||
databases,
|
||||
bucketName,
|
||||
backupName,
|
||||
),
|
||||
)
|
||||
|
||||
client.EXPECT().ApplyJob(jobDef).Times(1).Return(nil)
|
||||
client.EXPECT().GetJob(jobDef.Namespace, jobDef.Name).Times(1).Return(nil, macherrs.NewNotFound(schema.GroupResource{"batch", "jobs"}, jobName))
|
||||
client.EXPECT().WaitUntilJobCompleted(jobDef.Namespace, jobDef.Name, timeout).Times(1).Return(nil)
|
||||
client.EXPECT().DeleteJob(jobDef.Namespace, jobDef.Name).Times(1).Return(nil)
|
||||
|
||||
query, _, err := AdaptFunc(
|
||||
monitor,
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
databases,
|
||||
bucketName,
|
||||
timestamp,
|
||||
nodeselector,
|
||||
tolerations,
|
||||
checkDBReady,
|
||||
secretName,
|
||||
secretKey,
|
||||
version,
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
queried := map[string]interface{}{}
|
||||
ensure, err := query(client, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, ensure(client))
|
||||
}
|
25
operator/database/kinds/backups/bucket/restore/cleanup.go
Normal file
25
operator/database/kinds/backups/bucket/restore/cleanup.go
Normal file
@ -0,0 +1,25 @@
|
||||
package restore
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/zitadel/operator"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func getCleanupFunc(monitor mntr.Monitor, namespace, jobName string) operator.EnsureFunc {
|
||||
return func(k8sClient kubernetes.ClientInt) error {
|
||||
monitor.Info("waiting for restore to be completed")
|
||||
if err := k8sClient.WaitUntilJobCompleted(namespace, jobName, timeout); err != nil {
|
||||
monitor.Error(errors.Wrap(err, "error while waiting for restore to be completed"))
|
||||
return err
|
||||
}
|
||||
monitor.Info("restore is completed, cleanup")
|
||||
if err := k8sClient.DeleteJob(namespace, jobName); err != nil {
|
||||
monitor.Error(errors.Wrap(err, "error while trying to cleanup restore"))
|
||||
return err
|
||||
}
|
||||
monitor.Info("restore cleanup is completed")
|
||||
return nil
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package restore
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/caos/orbos/mntr"
|
||||
kubernetesmock "github.com/caos/orbos/pkg/kubernetes/mock"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBackup_Cleanup1(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
monitor := mntr.Monitor{}
|
||||
name := "test"
|
||||
namespace := "testNs"
|
||||
|
||||
cleanupFunc := getCleanupFunc(monitor, namespace, name)
|
||||
client.EXPECT().WaitUntilJobCompleted(namespace, name, timeout).Times(1).Return(nil)
|
||||
client.EXPECT().DeleteJob(namespace, name).Times(1)
|
||||
assert.NoError(t, cleanupFunc(client))
|
||||
|
||||
client.EXPECT().WaitUntilJobCompleted(namespace, name, timeout).Times(1).Return(errors.New("fail"))
|
||||
assert.Error(t, cleanupFunc(client))
|
||||
}
|
||||
|
||||
func TestBackup_Cleanup2(t *testing.T) {
|
||||
client := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
monitor := mntr.Monitor{}
|
||||
name := "test2"
|
||||
namespace := "testNs2"
|
||||
|
||||
cleanupFunc := getCleanupFunc(monitor, namespace, name)
|
||||
client.EXPECT().WaitUntilJobCompleted(namespace, name, timeout).Times(1).Return(nil)
|
||||
client.EXPECT().DeleteJob(namespace, name).Times(1)
|
||||
assert.NoError(t, cleanupFunc(client))
|
||||
|
||||
client.EXPECT().WaitUntilJobCompleted(namespace, name, timeout).Times(1).Return(errors.New("fail"))
|
||||
assert.Error(t, cleanupFunc(client))
|
||||
}
|
28
operator/database/kinds/backups/bucket/restore/command.go
Normal file
28
operator/database/kinds/backups/bucket/restore/command.go
Normal file
@ -0,0 +1,28 @@
|
||||
package restore
|
||||
|
||||
import "strings"
|
||||
|
||||
func getCommand(
|
||||
timestamp string,
|
||||
databases []string,
|
||||
bucketName string,
|
||||
backupName string,
|
||||
|
||||
) string {
|
||||
|
||||
backupCommands := make([]string, 0)
|
||||
for _, database := range databases {
|
||||
backupCommands = append(backupCommands,
|
||||
strings.Join([]string{
|
||||
"/scripts/restore.sh",
|
||||
bucketName,
|
||||
backupName,
|
||||
timestamp,
|
||||
database,
|
||||
secretPath,
|
||||
certPath,
|
||||
}, " "))
|
||||
}
|
||||
|
||||
return strings.Join(backupCommands, " && ")
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package restore
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBackup_Command1(t *testing.T) {
|
||||
timestamp := ""
|
||||
databases := []string{}
|
||||
bucketName := "testBucket"
|
||||
backupName := "testBackup"
|
||||
|
||||
cmd := getCommand(timestamp, databases, bucketName, backupName)
|
||||
equals := ""
|
||||
assert.Equal(t, equals, cmd)
|
||||
}
|
||||
|
||||
func TestBackup_Command2(t *testing.T) {
|
||||
timestamp := ""
|
||||
databases := []string{"testDb"}
|
||||
bucketName := "testBucket"
|
||||
backupName := "testBackup"
|
||||
|
||||
cmd := getCommand(timestamp, databases, bucketName, backupName)
|
||||
equals := "/scripts/restore.sh testBucket testBackup testDb /secrets/sa.json /cockroach/cockroach-certs"
|
||||
assert.Equal(t, equals, cmd)
|
||||
}
|
||||
|
||||
func TestBackup_Command3(t *testing.T) {
|
||||
timestamp := "test"
|
||||
databases := []string{"testDb"}
|
||||
bucketName := "testBucket"
|
||||
backupName := "testBackup"
|
||||
|
||||
cmd := getCommand(timestamp, databases, bucketName, backupName)
|
||||
equals := "/scripts/restore.sh testBucket testBackup test testDb /secrets/sa.json /cockroach/cockroach-certs"
|
||||
assert.Equal(t, equals, cmd)
|
||||
}
|
||||
|
||||
func TestBackup_Command4(t *testing.T) {
|
||||
timestamp := ""
|
||||
databases := []string{}
|
||||
bucketName := "test"
|
||||
backupName := "test"
|
||||
|
||||
cmd := getCommand(timestamp, databases, bucketName, backupName)
|
||||
equals := ""
|
||||
assert.Equal(t, equals, cmd)
|
||||
}
|
72
operator/database/kinds/backups/bucket/restore/job.go
Normal file
72
operator/database/kinds/backups/bucket/restore/job.go
Normal file
@ -0,0 +1,72 @@
|
||||
package restore
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/caos/zitadel/operator/helpers"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func getJob(
|
||||
namespace string,
|
||||
nameLabels *labels.Name,
|
||||
nodeselector map[string]string,
|
||||
tolerations []corev1.Toleration,
|
||||
secretName string,
|
||||
secretKey string,
|
||||
version string,
|
||||
command string,
|
||||
|
||||
) *batchv1.Job {
|
||||
return &batchv1.Job{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: nameLabels.Name(),
|
||||
Namespace: namespace,
|
||||
Labels: labels.MustK8sMap(nameLabels),
|
||||
},
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
NodeSelector: nodeselector,
|
||||
Tolerations: tolerations,
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
Containers: []corev1.Container{{
|
||||
Name: nameLabels.Name(),
|
||||
Image: image + ":" + version,
|
||||
Command: []string{
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
command,
|
||||
},
|
||||
VolumeMounts: []corev1.VolumeMount{{
|
||||
Name: internalSecretName,
|
||||
MountPath: certPath,
|
||||
}, {
|
||||
Name: secretKey,
|
||||
SubPath: secretKey,
|
||||
MountPath: secretPath,
|
||||
}},
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
}},
|
||||
Volumes: []corev1.Volume{{
|
||||
Name: internalSecretName,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: rootSecretName,
|
||||
DefaultMode: helpers.PointerInt32(defaultMode),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: secretKey,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: secretName,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
163
operator/database/kinds/backups/bucket/restore/job_test.go
Normal file
163
operator/database/kinds/backups/bucket/restore/job_test.go
Normal file
@ -0,0 +1,163 @@
|
||||
package restore
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/caos/zitadel/operator/helpers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBackup_Job1(t *testing.T) {
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey", Operator: "testOp"}}
|
||||
version := "testVersion"
|
||||
command := "test"
|
||||
secretKey := "testKey"
|
||||
secretName := "testSecretName"
|
||||
jobName := "testJob"
|
||||
namespace := "testNs"
|
||||
k8sLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "testComponent",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": jobName,
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"app.kubernetes.io/version": "testOpVersion",
|
||||
"caos.ch/apiversion": "testVersion",
|
||||
"caos.ch/kind": "testKind"}
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testOpVersion"), "testKind", "testVersion"), "testComponent")
|
||||
nameLabels := labels.MustForName(componentLabels, jobName)
|
||||
|
||||
equals :=
|
||||
&batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: jobName,
|
||||
Namespace: namespace,
|
||||
Labels: k8sLabels,
|
||||
},
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
NodeSelector: nodeselector,
|
||||
Tolerations: tolerations,
|
||||
Containers: []corev1.Container{{
|
||||
Name: jobName,
|
||||
Image: image + ":" + version,
|
||||
Command: []string{
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
command,
|
||||
},
|
||||
VolumeMounts: []corev1.VolumeMount{{
|
||||
Name: internalSecretName,
|
||||
MountPath: certPath,
|
||||
}, {
|
||||
Name: secretKey,
|
||||
SubPath: secretKey,
|
||||
MountPath: secretPath,
|
||||
}},
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
}},
|
||||
Volumes: []corev1.Volume{{
|
||||
Name: internalSecretName,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: rootSecretName,
|
||||
DefaultMode: helpers.PointerInt32(defaultMode),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: secretKey,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: secretName,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, equals, getJob(namespace, nameLabels, nodeselector, tolerations, secretName, secretKey, version, command))
|
||||
}
|
||||
|
||||
func TestBackup_Job2(t *testing.T) {
|
||||
nodeselector := map[string]string{"test2": "test2"}
|
||||
tolerations := []corev1.Toleration{
|
||||
{Key: "testKey2", Operator: "testOp2"}}
|
||||
version := "testVersion2"
|
||||
command := "test2"
|
||||
secretKey := "testKey2"
|
||||
secretName := "testSecretName2"
|
||||
jobName := "testJob2"
|
||||
namespace := "testNs2"
|
||||
k8sLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "testComponent2",
|
||||
"app.kubernetes.io/managed-by": "testOp2",
|
||||
"app.kubernetes.io/name": jobName,
|
||||
"app.kubernetes.io/part-of": "testProd2",
|
||||
"app.kubernetes.io/version": "testOpVersion2",
|
||||
"caos.ch/apiversion": "testVersion2",
|
||||
"caos.ch/kind": "testKind2"}
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd2", "testOp2", "testOpVersion2"), "testKind2", "testVersion2"), "testComponent2")
|
||||
nameLabels := labels.MustForName(componentLabels, jobName)
|
||||
|
||||
equals :=
|
||||
&batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: jobName,
|
||||
Namespace: namespace,
|
||||
Labels: k8sLabels,
|
||||
},
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
NodeSelector: nodeselector,
|
||||
Tolerations: tolerations,
|
||||
Containers: []corev1.Container{{
|
||||
Name: jobName,
|
||||
Image: image + ":" + version,
|
||||
Command: []string{
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
command,
|
||||
},
|
||||
VolumeMounts: []corev1.VolumeMount{{
|
||||
Name: internalSecretName,
|
||||
MountPath: certPath,
|
||||
}, {
|
||||
Name: secretKey,
|
||||
SubPath: secretKey,
|
||||
MountPath: secretPath,
|
||||
}},
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
}},
|
||||
Volumes: []corev1.Volume{{
|
||||
Name: internalSecretName,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: rootSecretName,
|
||||
DefaultMode: helpers.PointerInt32(defaultMode),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: secretKey,
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: secretName,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, equals, getJob(namespace, nameLabels, nodeselector, tolerations, secretName, secretKey, version, command))
|
||||
}
|
19
operator/database/kinds/backups/bucket/secrets.go
Normal file
19
operator/database/kinds/backups/bucket/secrets.go
Normal file
@ -0,0 +1,19 @@
|
||||
package bucket
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/pkg/secret"
|
||||
)
|
||||
|
||||
func getSecretsMap(desiredKind *DesiredV0) map[string]*secret.Secret {
|
||||
secrets := make(map[string]*secret.Secret, 0)
|
||||
if desiredKind.Spec == nil {
|
||||
desiredKind.Spec = &Spec{}
|
||||
}
|
||||
|
||||
if desiredKind.Spec.ServiceAccountJSON == nil {
|
||||
desiredKind.Spec.ServiceAccountJSON = &secret.Secret{}
|
||||
}
|
||||
secrets["serviceaccountjson"] = desiredKind.Spec.ServiceAccountJSON
|
||||
|
||||
return secrets
|
||||
}
|
22
operator/database/kinds/backups/bucket/secrets_test.go
Normal file
22
operator/database/kinds/backups/bucket/secrets_test.go
Normal file
@ -0,0 +1,22 @@
|
||||
package bucket
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/pkg/secret"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBucket_getSecretsFull(t *testing.T) {
|
||||
secrets := getSecretsMap(&desired)
|
||||
assert.Equal(t, desired.Spec.ServiceAccountJSON, secrets["serviceaccountjson"])
|
||||
}
|
||||
|
||||
func TestBucket_getSecretsEmpty(t *testing.T) {
|
||||
secrets := getSecretsMap(&desiredWithoutSecret)
|
||||
assert.Equal(t, &secret.Secret{}, secrets["serviceaccountjson"])
|
||||
}
|
||||
|
||||
func TestBucket_getSecretsNil(t *testing.T) {
|
||||
secrets := getSecretsMap(&desiredNil)
|
||||
assert.Equal(t, &secret.Secret{}, secrets["serviceaccountjson"])
|
||||
}
|
8
operator/database/kinds/backups/core/list.go
Normal file
8
operator/database/kinds/backups/core/list.go
Normal file
@ -0,0 +1,8 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
)
|
||||
|
||||
type BackupListFunc func(monitor mntr.Monitor, name string, desired *tree.Tree) ([]string, error)
|
64
operator/database/kinds/databases/core/current.go
Normal file
64
operator/database/kinds/databases/core/current.go
Normal file
@ -0,0 +1,64 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/caos/zitadel/operator"
|
||||
)
|
||||
|
||||
const queriedName = "database"
|
||||
|
||||
type DatabaseCurrent interface {
|
||||
GetURL() string
|
||||
GetPort() string
|
||||
GetReadyQuery() operator.EnsureFunc
|
||||
GetCertificateKey() *rsa.PrivateKey
|
||||
SetCertificateKey(*rsa.PrivateKey)
|
||||
GetCertificate() []byte
|
||||
SetCertificate([]byte)
|
||||
GetAddUserFunc() func(user string) (operator.QueryFunc, error)
|
||||
GetDeleteUserFunc() func(user string) (operator.DestroyFunc, error)
|
||||
GetListUsersFunc() func(k8sClient kubernetes.ClientInt) ([]string, error)
|
||||
GetListDatabasesFunc() func(k8sClient kubernetes.ClientInt) ([]string, error)
|
||||
}
|
||||
|
||||
func ParseQueriedForDatabase(queried map[string]interface{}) (DatabaseCurrent, error) {
|
||||
queriedDB, ok := queried[queriedName]
|
||||
if !ok {
|
||||
return nil, errors.New("no current state for database found")
|
||||
}
|
||||
currentDBTree, ok := queriedDB.(*tree.Tree)
|
||||
if !ok {
|
||||
return nil, errors.New("current state does not fullfil interface")
|
||||
}
|
||||
currentDB, ok := currentDBTree.Parsed.(DatabaseCurrent)
|
||||
if !ok {
|
||||
return nil, errors.New("current state does not fullfil interface")
|
||||
}
|
||||
|
||||
return currentDB, nil
|
||||
}
|
||||
|
||||
func SetQueriedForDatabase(queried map[string]interface{}, databaseCurrent *tree.Tree) {
|
||||
queried[queriedName] = databaseCurrent
|
||||
}
|
||||
|
||||
func SetQueriedForDatabaseDBList(queried map[string]interface{}, databases []string) {
|
||||
currentDBList := &CurrentDBList{
|
||||
Common: &tree.Common{
|
||||
Kind: "DBList",
|
||||
Version: "V0",
|
||||
},
|
||||
Current: &DatabaseCurrentDBList{
|
||||
Databases: databases,
|
||||
},
|
||||
}
|
||||
|
||||
currentDB := &tree.Tree{
|
||||
Parsed: currentDBList,
|
||||
}
|
||||
|
||||
SetQueriedForDatabase(queried, currentDB)
|
||||
}
|
65
operator/database/kinds/databases/core/dblist.go
Normal file
65
operator/database/kinds/databases/core/dblist.go
Normal file
@ -0,0 +1,65 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/caos/zitadel/operator"
|
||||
)
|
||||
|
||||
var current DatabaseCurrent = &CurrentDBList{}
|
||||
|
||||
type CurrentDBList struct {
|
||||
Common *tree.Common `yaml:",inline"`
|
||||
Current *DatabaseCurrentDBList
|
||||
}
|
||||
|
||||
type DatabaseCurrentDBList struct {
|
||||
Databases []string
|
||||
}
|
||||
|
||||
func (c *CurrentDBList) GetURL() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *CurrentDBList) GetPort() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *CurrentDBList) GetReadyQuery() operator.EnsureFunc {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CurrentDBList) GetCertificateKey() *rsa.PrivateKey {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CurrentDBList) SetCertificateKey(key *rsa.PrivateKey) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CurrentDBList) GetCertificate() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CurrentDBList) SetCertificate(cert []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *CurrentDBList) GetListDatabasesFunc() func(k8sClient kubernetes.ClientInt) ([]string, error) {
|
||||
return func(k8sClient kubernetes.ClientInt) ([]string, error) {
|
||||
return c.Current.Databases, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CurrentDBList) GetListUsersFunc() func(k8sClient kubernetes.ClientInt) ([]string, error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CurrentDBList) GetAddUserFunc() func(user string) (operator.QueryFunc, error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CurrentDBList) GetDeleteUserFunc() func(user string) (operator.DestroyFunc, error) {
|
||||
return nil
|
||||
}
|
3
operator/database/kinds/databases/core/generate.go
Normal file
3
operator/database/kinds/databases/core/generate.go
Normal file
@ -0,0 +1,3 @@
|
||||
package core
|
||||
|
||||
//go:generate mockgen -source current.go -package coremock -destination mock/current.mock.go github.com/caos/internal/operator/database/kinds/databases/core DatabaseCurrent
|
186
operator/database/kinds/databases/core/mock/current.mock.go
Normal file
186
operator/database/kinds/databases/core/mock/current.mock.go
Normal file
@ -0,0 +1,186 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: current.go
|
||||
|
||||
// Package coremock is a generated GoMock package.
|
||||
package coremock
|
||||
|
||||
import (
|
||||
rsa "crypto/rsa"
|
||||
kubernetes "github.com/caos/orbos/pkg/kubernetes"
|
||||
operator "github.com/caos/zitadel/operator"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockDatabaseCurrent is a mock of DatabaseCurrent interface
|
||||
type MockDatabaseCurrent struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockDatabaseCurrentMockRecorder
|
||||
}
|
||||
|
||||
// MockDatabaseCurrentMockRecorder is the mock recorder for MockDatabaseCurrent
|
||||
type MockDatabaseCurrentMockRecorder struct {
|
||||
mock *MockDatabaseCurrent
|
||||
}
|
||||
|
||||
// NewMockDatabaseCurrent creates a new mock instance
|
||||
func NewMockDatabaseCurrent(ctrl *gomock.Controller) *MockDatabaseCurrent {
|
||||
mock := &MockDatabaseCurrent{ctrl: ctrl}
|
||||
mock.recorder = &MockDatabaseCurrentMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockDatabaseCurrent) EXPECT() *MockDatabaseCurrentMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// GetURL mocks base method
|
||||
func (m *MockDatabaseCurrent) GetURL() string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetURL")
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetURL indicates an expected call of GetURL
|
||||
func (mr *MockDatabaseCurrentMockRecorder) GetURL() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetURL", reflect.TypeOf((*MockDatabaseCurrent)(nil).GetURL))
|
||||
}
|
||||
|
||||
// GetPort mocks base method
|
||||
func (m *MockDatabaseCurrent) GetPort() string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetPort")
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetPort indicates an expected call of GetPort
|
||||
func (mr *MockDatabaseCurrentMockRecorder) GetPort() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPort", reflect.TypeOf((*MockDatabaseCurrent)(nil).GetPort))
|
||||
}
|
||||
|
||||
// GetReadyQuery mocks base method
|
||||
func (m *MockDatabaseCurrent) GetReadyQuery() operator.EnsureFunc {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetReadyQuery")
|
||||
ret0, _ := ret[0].(operator.EnsureFunc)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetReadyQuery indicates an expected call of GetReadyQuery
|
||||
func (mr *MockDatabaseCurrentMockRecorder) GetReadyQuery() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReadyQuery", reflect.TypeOf((*MockDatabaseCurrent)(nil).GetReadyQuery))
|
||||
}
|
||||
|
||||
// GetCertificateKey mocks base method
|
||||
func (m *MockDatabaseCurrent) GetCertificateKey() *rsa.PrivateKey {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetCertificateKey")
|
||||
ret0, _ := ret[0].(*rsa.PrivateKey)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetCertificateKey indicates an expected call of GetCertificateKey
|
||||
func (mr *MockDatabaseCurrentMockRecorder) GetCertificateKey() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCertificateKey", reflect.TypeOf((*MockDatabaseCurrent)(nil).GetCertificateKey))
|
||||
}
|
||||
|
||||
// SetCertificateKey mocks base method
|
||||
func (m *MockDatabaseCurrent) SetCertificateKey(arg0 *rsa.PrivateKey) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "SetCertificateKey", arg0)
|
||||
}
|
||||
|
||||
// SetCertificateKey indicates an expected call of SetCertificateKey
|
||||
func (mr *MockDatabaseCurrentMockRecorder) SetCertificateKey(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCertificateKey", reflect.TypeOf((*MockDatabaseCurrent)(nil).SetCertificateKey), arg0)
|
||||
}
|
||||
|
||||
// GetCertificate mocks base method
|
||||
func (m *MockDatabaseCurrent) GetCertificate() []byte {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetCertificate")
|
||||
ret0, _ := ret[0].([]byte)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetCertificate indicates an expected call of GetCertificate
|
||||
func (mr *MockDatabaseCurrentMockRecorder) GetCertificate() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCertificate", reflect.TypeOf((*MockDatabaseCurrent)(nil).GetCertificate))
|
||||
}
|
||||
|
||||
// SetCertificate mocks base method
|
||||
func (m *MockDatabaseCurrent) SetCertificate(arg0 []byte) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "SetCertificate", arg0)
|
||||
}
|
||||
|
||||
// SetCertificate indicates an expected call of SetCertificate
|
||||
func (mr *MockDatabaseCurrentMockRecorder) SetCertificate(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCertificate", reflect.TypeOf((*MockDatabaseCurrent)(nil).SetCertificate), arg0)
|
||||
}
|
||||
|
||||
// GetAddUserFunc mocks base method
|
||||
func (m *MockDatabaseCurrent) GetAddUserFunc() func(string) (operator.QueryFunc, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetAddUserFunc")
|
||||
ret0, _ := ret[0].(func(string) (operator.QueryFunc, error))
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetAddUserFunc indicates an expected call of GetAddUserFunc
|
||||
func (mr *MockDatabaseCurrentMockRecorder) GetAddUserFunc() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAddUserFunc", reflect.TypeOf((*MockDatabaseCurrent)(nil).GetAddUserFunc))
|
||||
}
|
||||
|
||||
// GetDeleteUserFunc mocks base method
|
||||
func (m *MockDatabaseCurrent) GetDeleteUserFunc() func(string) (operator.DestroyFunc, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetDeleteUserFunc")
|
||||
ret0, _ := ret[0].(func(string) (operator.DestroyFunc, error))
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetDeleteUserFunc indicates an expected call of GetDeleteUserFunc
|
||||
func (mr *MockDatabaseCurrentMockRecorder) GetDeleteUserFunc() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeleteUserFunc", reflect.TypeOf((*MockDatabaseCurrent)(nil).GetDeleteUserFunc))
|
||||
}
|
||||
|
||||
// GetListUsersFunc mocks base method
|
||||
func (m *MockDatabaseCurrent) GetListUsersFunc() func(kubernetes.ClientInt) ([]string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetListUsersFunc")
|
||||
ret0, _ := ret[0].(func(kubernetes.ClientInt) ([]string, error))
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetListUsersFunc indicates an expected call of GetListUsersFunc
|
||||
func (mr *MockDatabaseCurrentMockRecorder) GetListUsersFunc() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListUsersFunc", reflect.TypeOf((*MockDatabaseCurrent)(nil).GetListUsersFunc))
|
||||
}
|
||||
|
||||
// GetListDatabasesFunc mocks base method
|
||||
func (m *MockDatabaseCurrent) GetListDatabasesFunc() func(kubernetes.ClientInt) ([]string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetListDatabasesFunc")
|
||||
ret0, _ := ret[0].(func(kubernetes.ClientInt) ([]string, error))
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetListDatabasesFunc indicates an expected call of GetListDatabasesFunc
|
||||
func (mr *MockDatabaseCurrentMockRecorder) GetListDatabasesFunc() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListDatabasesFunc", reflect.TypeOf((*MockDatabaseCurrent)(nil).GetListDatabasesFunc))
|
||||
}
|
68
operator/database/kinds/databases/databases.go
Normal file
68
operator/database/kinds/databases/databases.go
Normal file
@ -0,0 +1,68 @@
|
||||
package databases
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/caos/orbos/pkg/secret"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/caos/zitadel/operator"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/managed"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/provided"
|
||||
"github.com/pkg/errors"
|
||||
core "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
component = "database"
|
||||
)
|
||||
|
||||
func ComponentSelector() *labels.Selector {
|
||||
return labels.OpenComponentSelector("ZITADEL", component)
|
||||
}
|
||||
|
||||
func GetQueryAndDestroyFuncs(
|
||||
monitor mntr.Monitor,
|
||||
desiredTree *tree.Tree,
|
||||
currentTree *tree.Tree,
|
||||
namespace string,
|
||||
apiLabels *labels.API,
|
||||
timestamp string,
|
||||
nodeselector map[string]string,
|
||||
tolerations []core.Toleration,
|
||||
version string,
|
||||
features []string,
|
||||
) (
|
||||
query operator.QueryFunc,
|
||||
destroy operator.DestroyFunc,
|
||||
secrets map[string]*secret.Secret,
|
||||
err error,
|
||||
) {
|
||||
componentLabels := labels.MustForComponent(apiLabels, component)
|
||||
internalMonitor := monitor.WithField("component", component)
|
||||
|
||||
switch desiredTree.Common.Kind {
|
||||
case "databases.caos.ch/CockroachDB":
|
||||
return managed.AdaptFunc(componentLabels, namespace, timestamp, nodeselector, tolerations, version, features)(internalMonitor, desiredTree, currentTree)
|
||||
case "databases.caos.ch/ProvidedDatabase":
|
||||
return provided.AdaptFunc()(internalMonitor, desiredTree, currentTree)
|
||||
default:
|
||||
return nil, nil, nil, errors.Errorf("unknown database kind %s", desiredTree.Common.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
func GetBackupList(
|
||||
monitor mntr.Monitor,
|
||||
desiredTree *tree.Tree,
|
||||
) (
|
||||
[]string,
|
||||
error,
|
||||
) {
|
||||
switch desiredTree.Common.Kind {
|
||||
case "databases.caos.ch/CockroachDB":
|
||||
return managed.BackupList()(monitor, desiredTree)
|
||||
case "databases.caos.ch/ProvidedDatabse":
|
||||
return nil, errors.Errorf("no backups supported for database kind %s", desiredTree.Common.Kind)
|
||||
default:
|
||||
return nil, errors.Errorf("unknown database kind %s", desiredTree.Common.Kind)
|
||||
}
|
||||
}
|
256
operator/database/kinds/databases/managed/adapt.go
Normal file
256
operator/database/kinds/databases/managed/adapt.go
Normal file
@ -0,0 +1,256 @@
|
||||
package managed
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/operator"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
|
||||
"github.com/caos/orbos/pkg/secret"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/managed/certificate"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/orbos/pkg/kubernetes/resources/pdb"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/core"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/managed/rbac"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/managed/services"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/managed/statefulset"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
SfsName = "cockroachdb"
|
||||
pdbName = SfsName + "-budget"
|
||||
serviceAccountName = SfsName
|
||||
PublicServiceName = SfsName + "-public"
|
||||
privateServiceName = SfsName
|
||||
cockroachPort = int32(26257)
|
||||
cockroachHTTPPort = int32(8080)
|
||||
image = "cockroachdb/cockroach:v20.2.3"
|
||||
)
|
||||
|
||||
func AdaptFunc(
|
||||
componentLabels *labels.Component,
|
||||
namespace string,
|
||||
timestamp string,
|
||||
nodeselector map[string]string,
|
||||
tolerations []corev1.Toleration,
|
||||
version string,
|
||||
features []string,
|
||||
) func(
|
||||
monitor mntr.Monitor,
|
||||
desired *tree.Tree,
|
||||
current *tree.Tree,
|
||||
) (
|
||||
operator.QueryFunc,
|
||||
operator.DestroyFunc,
|
||||
map[string]*secret.Secret,
|
||||
error,
|
||||
) {
|
||||
|
||||
return func(
|
||||
monitor mntr.Monitor,
|
||||
desired *tree.Tree,
|
||||
current *tree.Tree,
|
||||
) (
|
||||
operator.QueryFunc,
|
||||
operator.DestroyFunc,
|
||||
map[string]*secret.Secret,
|
||||
error,
|
||||
) {
|
||||
internalMonitor := monitor.WithField("kind", "cockroachdb")
|
||||
allSecrets := map[string]*secret.Secret{}
|
||||
|
||||
desiredKind, err := parseDesiredV0(desired)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(err, "parsing desired state failed")
|
||||
}
|
||||
desired.Parsed = desiredKind
|
||||
|
||||
if !monitor.IsVerbose() && desiredKind.Spec.Verbose {
|
||||
internalMonitor.Verbose()
|
||||
}
|
||||
|
||||
var (
|
||||
isFeatureDatabase bool
|
||||
isFeatureRestore bool
|
||||
)
|
||||
for _, feature := range features {
|
||||
switch feature {
|
||||
case "database":
|
||||
isFeatureDatabase = true
|
||||
case "restore":
|
||||
isFeatureRestore = true
|
||||
}
|
||||
}
|
||||
|
||||
queryCert, destroyCert, addUser, deleteUser, listUsers, err := certificate.AdaptFunc(internalMonitor, namespace, componentLabels, desiredKind.Spec.ClusterDns, isFeatureDatabase)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
addRoot, err := addUser("root")
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
destroyRoot, err := deleteUser("root")
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
queryRBAC, destroyRBAC, err := rbac.AdaptFunc(internalMonitor, namespace, labels.MustForName(componentLabels, serviceAccountName))
|
||||
|
||||
cockroachNameLabels := labels.MustForName(componentLabels, SfsName)
|
||||
cockroachSelector := labels.DeriveNameSelector(cockroachNameLabels, false)
|
||||
cockroachSelectabel := labels.AsSelectable(cockroachNameLabels)
|
||||
querySFS, destroySFS, ensureInit, checkDBReady, listDatabases, err := statefulset.AdaptFunc(
|
||||
internalMonitor,
|
||||
cockroachSelectabel,
|
||||
cockroachSelector,
|
||||
desiredKind.Spec.Force,
|
||||
namespace,
|
||||
image,
|
||||
serviceAccountName,
|
||||
desiredKind.Spec.ReplicaCount,
|
||||
desiredKind.Spec.StorageCapacity,
|
||||
cockroachPort,
|
||||
cockroachHTTPPort,
|
||||
desiredKind.Spec.StorageClass,
|
||||
desiredKind.Spec.NodeSelector,
|
||||
desiredKind.Spec.Tolerations,
|
||||
desiredKind.Spec.Resources,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
queryS, destroyS, err := services.AdaptFunc(
|
||||
internalMonitor,
|
||||
namespace,
|
||||
labels.MustForName(componentLabels, PublicServiceName),
|
||||
labels.MustForName(componentLabels, privateServiceName),
|
||||
cockroachSelector,
|
||||
cockroachPort,
|
||||
cockroachHTTPPort,
|
||||
)
|
||||
|
||||
//externalName := "cockroachdb-public." + namespaceStr + ".svc.cluster.local"
|
||||
//queryES, destroyES, err := service.AdaptFunc("cockroachdb-public", "default", labels, []service.Port{}, "ExternalName", map[string]string{}, false, "", externalName)
|
||||
//if err != nil {
|
||||
// return nil, nil, err
|
||||
//}
|
||||
|
||||
queryPDB, err := pdb.AdaptFuncToEnsure(namespace, labels.MustForName(componentLabels, pdbName), cockroachSelector, "1")
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
destroyPDB, err := pdb.AdaptFuncToDestroy(namespace, pdbName)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
currentDB := &Current{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/CockroachDB",
|
||||
Version: "v0",
|
||||
},
|
||||
Current: &CurrentDB{
|
||||
CA: &certificate.Current{},
|
||||
},
|
||||
}
|
||||
current.Parsed = currentDB
|
||||
|
||||
queriers := make([]operator.QueryFunc, 0)
|
||||
if isFeatureDatabase {
|
||||
queriers = append(queriers,
|
||||
queryRBAC,
|
||||
queryCert,
|
||||
addRoot,
|
||||
operator.ResourceQueryToZitadelQuery(querySFS),
|
||||
operator.ResourceQueryToZitadelQuery(queryPDB),
|
||||
queryS,
|
||||
operator.EnsureFuncToQueryFunc(ensureInit),
|
||||
)
|
||||
}
|
||||
|
||||
destroyers := make([]operator.DestroyFunc, 0)
|
||||
if isFeatureDatabase {
|
||||
destroyers = append(destroyers,
|
||||
operator.ResourceDestroyToZitadelDestroy(destroyPDB),
|
||||
destroyS,
|
||||
operator.ResourceDestroyToZitadelDestroy(destroySFS),
|
||||
destroyRBAC,
|
||||
destroyCert,
|
||||
destroyRoot,
|
||||
)
|
||||
}
|
||||
|
||||
if desiredKind.Spec.Backups != nil {
|
||||
|
||||
oneBackup := false
|
||||
for backupName := range desiredKind.Spec.Backups {
|
||||
if timestamp != "" && strings.HasPrefix(timestamp, backupName) {
|
||||
oneBackup = true
|
||||
}
|
||||
}
|
||||
|
||||
for backupName, desiredBackup := range desiredKind.Spec.Backups {
|
||||
currentBackup := &tree.Tree{}
|
||||
if timestamp == "" || !oneBackup || (timestamp != "" && strings.HasPrefix(timestamp, backupName)) {
|
||||
queryB, destroyB, secrets, err := backups.GetQueryAndDestroyFuncs(
|
||||
internalMonitor,
|
||||
desiredBackup,
|
||||
currentBackup,
|
||||
backupName,
|
||||
namespace,
|
||||
componentLabels,
|
||||
checkDBReady,
|
||||
strings.TrimPrefix(timestamp, backupName+"."),
|
||||
nodeselector,
|
||||
tolerations,
|
||||
version,
|
||||
features,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
secret.AppendSecrets(backupName, allSecrets, secrets)
|
||||
destroyers = append(destroyers, destroyB)
|
||||
queriers = append(queriers, queryB)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return func(k8sClient kubernetes.ClientInt, queried map[string]interface{}) (operator.EnsureFunc, error) {
|
||||
if !isFeatureRestore {
|
||||
queriedCurrentDB, err := core.ParseQueriedForDatabase(queried)
|
||||
if err != nil || queriedCurrentDB == nil {
|
||||
// TODO: query system state
|
||||
currentDB.Current.Port = strconv.Itoa(int(cockroachPort))
|
||||
currentDB.Current.URL = PublicServiceName
|
||||
currentDB.Current.ReadyFunc = checkDBReady
|
||||
currentDB.Current.AddUserFunc = addUser
|
||||
currentDB.Current.DeleteUserFunc = deleteUser
|
||||
currentDB.Current.ListUsersFunc = listUsers
|
||||
currentDB.Current.ListDatabasesFunc = listDatabases
|
||||
|
||||
core.SetQueriedForDatabase(queried, current)
|
||||
internalMonitor.Info("set current state of managed database")
|
||||
}
|
||||
}
|
||||
|
||||
ensure, err := operator.QueriersToEnsureFunc(internalMonitor, true, queriers, k8sClient, queried)
|
||||
return ensure, err
|
||||
},
|
||||
operator.DestroyersToDestroyFunc(internalMonitor, destroyers),
|
||||
allSecrets,
|
||||
nil
|
||||
}
|
||||
}
|
177
operator/database/kinds/databases/managed/adapt_backup_test.go
Normal file
177
operator/database/kinds/databases/managed/adapt_backup_test.go
Normal file
@ -0,0 +1,177 @@
|
||||
package managed
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
kubernetesmock "github.com/caos/orbos/pkg/kubernetes/mock"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/caos/orbos/pkg/secret"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket/backup"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket/clean"
|
||||
"github.com/caos/zitadel/operator/database/kinds/backups/bucket/restore"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func getTreeWithDBAndBackup(t *testing.T, masterkey string, saJson string, backupName string) *tree.Tree {
|
||||
|
||||
bucketDesired := getDesiredTree(t, masterkey, &bucket.DesiredV0{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/BucketBackup",
|
||||
Version: "v0",
|
||||
},
|
||||
Spec: &bucket.Spec{
|
||||
Verbose: true,
|
||||
Cron: "testCron",
|
||||
Bucket: "testBucket",
|
||||
ServiceAccountJSON: &secret.Secret{
|
||||
Value: saJson,
|
||||
},
|
||||
},
|
||||
})
|
||||
bucketDesiredKind, err := bucket.ParseDesiredV0(bucketDesired)
|
||||
assert.NoError(t, err)
|
||||
bucketDesired.Parsed = bucketDesiredKind
|
||||
|
||||
return getDesiredTree(t, masterkey, &DesiredV0{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/CockroachDB",
|
||||
Version: "v0",
|
||||
},
|
||||
Spec: Spec{
|
||||
Verbose: false,
|
||||
ReplicaCount: 1,
|
||||
StorageCapacity: "368Gi",
|
||||
StorageClass: "testSC",
|
||||
NodeSelector: map[string]string{},
|
||||
ClusterDns: "testDns",
|
||||
Backups: map[string]*tree.Tree{backupName: bucketDesired},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestManaged_AdaptBucketBackup(t *testing.T) {
|
||||
monitor := mntr.Monitor{}
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "testKind", "v0"), "database")
|
||||
|
||||
labels := map[string]string{
|
||||
"app.kubernetes.io/component": "backup",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": "backup-serviceaccountjson",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"app.kubernetes.io/version": "testVersion",
|
||||
"caos.ch/apiversion": "v0",
|
||||
"caos.ch/kind": "BucketBackup",
|
||||
}
|
||||
namespace := "testNs"
|
||||
timestamp := "testTs"
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{}
|
||||
version := "testVersion"
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
backupName := "testBucket"
|
||||
saJson := "testSA"
|
||||
masterkey := "testMk"
|
||||
|
||||
desired := getTreeWithDBAndBackup(t, masterkey, saJson, backupName)
|
||||
|
||||
features := []string{backup.Normal}
|
||||
bucket.SetBackup(k8sClient, namespace, labels, saJson)
|
||||
k8sClient.EXPECT().WaitUntilStatefulsetIsReady(namespace, SfsName, true, true, time.Duration(60))
|
||||
|
||||
query, _, _, err := AdaptFunc(componentLabels, namespace, timestamp, nodeselector, tolerations, version, features)(monitor, desired, &tree.Tree{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
databases := []string{"test1", "test2"}
|
||||
queried := bucket.SetQueriedForDatabases(databases)
|
||||
ensure, err := query(k8sClient, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ensure)
|
||||
|
||||
assert.NoError(t, ensure(k8sClient))
|
||||
}
|
||||
|
||||
func TestManaged_AdaptBucketInstantBackup(t *testing.T) {
|
||||
monitor := mntr.Monitor{}
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "testKind", "v0"), "database")
|
||||
labels := map[string]string{
|
||||
"app.kubernetes.io/component": "backup",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": "backup-serviceaccountjson",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"app.kubernetes.io/version": "testVersion",
|
||||
"caos.ch/apiversion": "v0",
|
||||
"caos.ch/kind": "BucketBackup",
|
||||
}
|
||||
namespace := "testNs"
|
||||
timestamp := "testTs"
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{}
|
||||
version := "testVersion"
|
||||
masterkey := "testMk"
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
saJson := "testSA"
|
||||
backupName := "testBucket"
|
||||
|
||||
features := []string{backup.Instant}
|
||||
bucket.SetInstantBackup(k8sClient, namespace, backupName, labels, saJson)
|
||||
k8sClient.EXPECT().WaitUntilStatefulsetIsReady(namespace, SfsName, true, true, time.Duration(60))
|
||||
|
||||
desired := getTreeWithDBAndBackup(t, masterkey, saJson, backupName)
|
||||
|
||||
query, _, _, err := AdaptFunc(componentLabels, namespace, timestamp, nodeselector, tolerations, version, features)(monitor, desired, &tree.Tree{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
databases := []string{"test1", "test2"}
|
||||
queried := bucket.SetQueriedForDatabases(databases)
|
||||
ensure, err := query(k8sClient, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ensure)
|
||||
|
||||
assert.NoError(t, ensure(k8sClient))
|
||||
}
|
||||
|
||||
func TestManaged_AdaptBucketCleanAndRestore(t *testing.T) {
|
||||
monitor := mntr.Monitor{}
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "testKind", "v0"), "database")
|
||||
labels := map[string]string{
|
||||
"app.kubernetes.io/component": "backup",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": "backup-serviceaccountjson",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"app.kubernetes.io/version": "testVersion",
|
||||
"caos.ch/apiversion": "v0",
|
||||
"caos.ch/kind": "BucketBackup",
|
||||
}
|
||||
namespace := "testNs"
|
||||
timestamp := "testTs"
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{}
|
||||
version := "testVersion"
|
||||
masterkey := "testMk"
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
saJson := "testSA"
|
||||
backupName := "testBucket"
|
||||
|
||||
features := []string{restore.Instant, clean.Instant}
|
||||
bucket.SetRestore(k8sClient, namespace, backupName, labels, saJson)
|
||||
bucket.SetClean(k8sClient, namespace, backupName)
|
||||
k8sClient.EXPECT().WaitUntilStatefulsetIsReady(namespace, SfsName, true, true, time.Duration(60)).Times(2)
|
||||
|
||||
desired := getTreeWithDBAndBackup(t, masterkey, saJson, backupName)
|
||||
|
||||
query, _, _, err := AdaptFunc(componentLabels, namespace, timestamp, nodeselector, tolerations, version, features)(monitor, desired, &tree.Tree{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
databases := []string{"test1", "test2"}
|
||||
queried := bucket.SetQueriedForDatabases(databases)
|
||||
ensure, err := query(k8sClient, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ensure)
|
||||
|
||||
assert.NoError(t, ensure(k8sClient))
|
||||
}
|
255
operator/database/kinds/databases/managed/adapt_test.go
Normal file
255
operator/database/kinds/databases/managed/adapt_test.go
Normal file
@ -0,0 +1,255 @@
|
||||
package managed
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/caos/orbos/mntr"
|
||||
kubernetesmock "github.com/caos/orbos/pkg/kubernetes/mock"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/caos/orbos/pkg/secret"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
coremock "github.com/caos/zitadel/operator/database/kinds/databases/core/mock"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
policy "k8s.io/api/policy/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
func getDesiredTree(t *testing.T, masterkey string, desired interface{}) *tree.Tree {
|
||||
secret.Masterkey = masterkey
|
||||
|
||||
desiredTree := &tree.Tree{}
|
||||
data, err := yaml.Marshal(desired)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, yaml.Unmarshal(data, desiredTree))
|
||||
|
||||
return desiredTree
|
||||
}
|
||||
|
||||
func TestManaged_Adapt1(t *testing.T) {
|
||||
monitor := mntr.Monitor{}
|
||||
|
||||
nodeLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "database",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": "cockroachdb.node",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"orbos.ch/selectable": "yes",
|
||||
}
|
||||
|
||||
cockroachLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "database",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": "cockroachdb-budget",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"app.kubernetes.io/version": "testVersion",
|
||||
"caos.ch/apiversion": "v0",
|
||||
"caos.ch/kind": "testKind",
|
||||
}
|
||||
|
||||
cockroachSelectorLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "database",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": "cockroachdb",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"orbos.ch/selectable": "yes",
|
||||
}
|
||||
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "testKind", "v0"), "database")
|
||||
|
||||
namespace := "testNs"
|
||||
timestamp := "testTs"
|
||||
nodeselector := map[string]string{"test": "test"}
|
||||
tolerations := []corev1.Toleration{}
|
||||
version := "testVersion"
|
||||
features := []string{"database"}
|
||||
masterkey := "testMk"
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
dbCurrent := coremock.NewMockDatabaseCurrent(gomock.NewController(t))
|
||||
queried := map[string]interface{}{}
|
||||
|
||||
desired := getDesiredTree(t, masterkey, &DesiredV0{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/CockroachDB",
|
||||
Version: "v0",
|
||||
},
|
||||
Spec: Spec{
|
||||
Verbose: false,
|
||||
ReplicaCount: 1,
|
||||
StorageCapacity: "368Gi",
|
||||
StorageClass: "testSC",
|
||||
NodeSelector: map[string]string{},
|
||||
ClusterDns: "testDns",
|
||||
},
|
||||
})
|
||||
|
||||
unav := intstr.FromInt(1)
|
||||
k8sClient.EXPECT().ApplyPodDisruptionBudget(&policy.PodDisruptionBudget{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cockroachdb-budget",
|
||||
Namespace: namespace,
|
||||
Labels: cockroachLabels,
|
||||
},
|
||||
Spec: policy.PodDisruptionBudgetSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: cockroachSelectorLabels,
|
||||
},
|
||||
MaxUnavailable: &unav,
|
||||
},
|
||||
})
|
||||
secretList := &corev1.SecretList{
|
||||
Items: []corev1.Secret{},
|
||||
}
|
||||
|
||||
k8sClient.EXPECT().ApplyService(gomock.Any()).Times(3)
|
||||
k8sClient.EXPECT().ApplyServiceAccount(gomock.Any()).Times(1)
|
||||
k8sClient.EXPECT().ApplyRole(gomock.Any()).Times(1)
|
||||
k8sClient.EXPECT().ApplyClusterRole(gomock.Any()).Times(1)
|
||||
k8sClient.EXPECT().ApplyRoleBinding(gomock.Any()).Times(1)
|
||||
k8sClient.EXPECT().ApplyClusterRoleBinding(gomock.Any()).Times(1)
|
||||
//statefulset
|
||||
k8sClient.EXPECT().ApplyStatefulSet(gomock.Any(), gomock.Any()).Times(1)
|
||||
//running for setup
|
||||
k8sClient.EXPECT().WaitUntilStatefulsetIsReady(namespace, SfsName, true, false, time.Duration(60))
|
||||
//not ready for setup
|
||||
k8sClient.EXPECT().WaitUntilStatefulsetIsReady(namespace, SfsName, true, true, time.Duration(1))
|
||||
//ready after setup
|
||||
k8sClient.EXPECT().WaitUntilStatefulsetIsReady(namespace, SfsName, true, true, time.Duration(60))
|
||||
//client
|
||||
k8sClient.EXPECT().ListSecrets(namespace, nodeLabels).Times(1).Return(secretList, nil)
|
||||
dbCurrent.EXPECT().GetCertificate().Times(1).Return(nil)
|
||||
dbCurrent.EXPECT().GetCertificateKey().Times(1).Return(nil)
|
||||
k8sClient.EXPECT().ApplySecret(gomock.Any()).Times(1)
|
||||
//node
|
||||
k8sClient.EXPECT().ListSecrets(namespace, nodeLabels).Times(1).Return(secretList, nil)
|
||||
dbCurrent.EXPECT().GetCertificate().Times(1).Return(nil)
|
||||
dbCurrent.EXPECT().GetCertificateKey().Times(1).Return(nil)
|
||||
dbCurrent.EXPECT().SetCertificate(gomock.Any()).Times(1)
|
||||
dbCurrent.EXPECT().SetCertificateKey(gomock.Any()).Times(1)
|
||||
k8sClient.EXPECT().ApplySecret(gomock.Any()).Times(1)
|
||||
|
||||
query, _, _, err := AdaptFunc(componentLabels, namespace, timestamp, nodeselector, tolerations, version, features)(monitor, desired, &tree.Tree{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
ensure, err := query(k8sClient, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ensure)
|
||||
|
||||
assert.NoError(t, ensure(k8sClient))
|
||||
}
|
||||
|
||||
func TestManaged_Adapt2(t *testing.T) {
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs"
|
||||
timestamp := "testTs"
|
||||
|
||||
nodeLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "database2",
|
||||
"app.kubernetes.io/managed-by": "testOp2",
|
||||
"app.kubernetes.io/name": "cockroachdb.node",
|
||||
"app.kubernetes.io/part-of": "testProd2",
|
||||
"orbos.ch/selectable": "yes",
|
||||
}
|
||||
|
||||
cockroachLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "database2",
|
||||
"app.kubernetes.io/managed-by": "testOp2",
|
||||
"app.kubernetes.io/name": "cockroachdb-budget",
|
||||
"app.kubernetes.io/part-of": "testProd2",
|
||||
"app.kubernetes.io/version": "testVersion2",
|
||||
"caos.ch/apiversion": "v1",
|
||||
"caos.ch/kind": "testKind2",
|
||||
}
|
||||
|
||||
cockroachSelectorLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "database2",
|
||||
"app.kubernetes.io/managed-by": "testOp2",
|
||||
"app.kubernetes.io/name": "cockroachdb",
|
||||
"app.kubernetes.io/part-of": "testProd2",
|
||||
"orbos.ch/selectable": "yes",
|
||||
}
|
||||
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd2", "testOp2", "testVersion2"), "testKind2", "v1"), "database2")
|
||||
|
||||
nodeselector := map[string]string{"test2": "test2"}
|
||||
var tolerations []corev1.Toleration
|
||||
version := "testVersion2"
|
||||
features := []string{"database"}
|
||||
masterkey := "testMk2"
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
dbCurrent := coremock.NewMockDatabaseCurrent(gomock.NewController(t))
|
||||
queried := map[string]interface{}{}
|
||||
|
||||
desired := getDesiredTree(t, masterkey, &DesiredV0{
|
||||
Common: &tree.Common{
|
||||
Kind: "databases.caos.ch/CockroachDB",
|
||||
Version: "v0",
|
||||
},
|
||||
Spec: Spec{
|
||||
Verbose: false,
|
||||
ReplicaCount: 1,
|
||||
StorageCapacity: "368Gi",
|
||||
StorageClass: "testSC",
|
||||
NodeSelector: map[string]string{},
|
||||
ClusterDns: "testDns",
|
||||
},
|
||||
})
|
||||
|
||||
unav := intstr.FromInt(1)
|
||||
k8sClient.EXPECT().ApplyPodDisruptionBudget(&policy.PodDisruptionBudget{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cockroachdb-budget",
|
||||
Namespace: namespace,
|
||||
Labels: cockroachLabels,
|
||||
},
|
||||
Spec: policy.PodDisruptionBudgetSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: cockroachSelectorLabels,
|
||||
},
|
||||
MaxUnavailable: &unav,
|
||||
},
|
||||
})
|
||||
secretList := &corev1.SecretList{
|
||||
Items: []corev1.Secret{},
|
||||
}
|
||||
|
||||
k8sClient.EXPECT().ApplyService(gomock.Any()).Times(3)
|
||||
k8sClient.EXPECT().ApplyServiceAccount(gomock.Any()).Times(1)
|
||||
k8sClient.EXPECT().ApplyRole(gomock.Any()).Times(1)
|
||||
k8sClient.EXPECT().ApplyClusterRole(gomock.Any()).Times(1)
|
||||
k8sClient.EXPECT().ApplyRoleBinding(gomock.Any()).Times(1)
|
||||
k8sClient.EXPECT().ApplyClusterRoleBinding(gomock.Any()).Times(1)
|
||||
//statefulset
|
||||
k8sClient.EXPECT().ApplyStatefulSet(gomock.Any(), gomock.Any()).Times(1)
|
||||
//running for setup
|
||||
k8sClient.EXPECT().WaitUntilStatefulsetIsReady(namespace, SfsName, true, false, time.Duration(60))
|
||||
//not ready for setup
|
||||
k8sClient.EXPECT().WaitUntilStatefulsetIsReady(namespace, SfsName, true, true, time.Duration(1))
|
||||
//ready after setup
|
||||
k8sClient.EXPECT().WaitUntilStatefulsetIsReady(namespace, SfsName, true, true, time.Duration(60))
|
||||
//client
|
||||
k8sClient.EXPECT().ListSecrets(namespace, nodeLabels).Times(1).Return(secretList, nil)
|
||||
dbCurrent.EXPECT().GetCertificate().Times(1).Return(nil)
|
||||
dbCurrent.EXPECT().GetCertificateKey().Times(1).Return(nil)
|
||||
k8sClient.EXPECT().ApplySecret(gomock.Any()).Times(1)
|
||||
//node
|
||||
k8sClient.EXPECT().ListSecrets(namespace, nodeLabels).Times(1).Return(secretList, nil)
|
||||
dbCurrent.EXPECT().GetCertificate().Times(1).Return(nil)
|
||||
dbCurrent.EXPECT().GetCertificateKey().Times(1).Return(nil)
|
||||
dbCurrent.EXPECT().SetCertificate(gomock.Any()).Times(1)
|
||||
dbCurrent.EXPECT().SetCertificateKey(gomock.Any()).Times(1)
|
||||
k8sClient.EXPECT().ApplySecret(gomock.Any()).Times(1)
|
||||
|
||||
query, _, _, err := AdaptFunc(componentLabels, namespace, timestamp, nodeselector, tolerations, version, features)(monitor, desired, &tree.Tree{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
ensure, err := query(k8sClient, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ensure)
|
||||
|
||||
assert.NoError(t, ensure(k8sClient))
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/caos/zitadel/operator"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/managed/certificate/client"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/managed/certificate/node"
|
||||
)
|
||||
|
||||
var (
|
||||
nodeSecret = "cockroachdb.node"
|
||||
)
|
||||
|
||||
func AdaptFunc(
|
||||
monitor mntr.Monitor,
|
||||
namespace string,
|
||||
componentLabels *labels.Component,
|
||||
clusterDns string,
|
||||
generateNodeIfNotExists bool,
|
||||
) (
|
||||
operator.QueryFunc,
|
||||
operator.DestroyFunc,
|
||||
func(user string) (operator.QueryFunc, error),
|
||||
func(user string) (operator.DestroyFunc, error),
|
||||
func(k8sClient kubernetes.ClientInt) ([]string, error),
|
||||
error,
|
||||
) {
|
||||
cMonitor := monitor.WithField("type", "certificates")
|
||||
|
||||
queryNode, destroyNode, err := node.AdaptFunc(
|
||||
cMonitor,
|
||||
namespace,
|
||||
labels.MustForName(componentLabels, nodeSecret),
|
||||
clusterDns,
|
||||
generateNodeIfNotExists,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
queriers := []operator.QueryFunc{
|
||||
queryNode,
|
||||
}
|
||||
|
||||
destroyers := []operator.DestroyFunc{
|
||||
destroyNode,
|
||||
}
|
||||
|
||||
return func(k8sClient kubernetes.ClientInt, queried map[string]interface{}) (operator.EnsureFunc, error) {
|
||||
return operator.QueriersToEnsureFunc(cMonitor, false, queriers, k8sClient, queried)
|
||||
},
|
||||
operator.DestroyersToDestroyFunc(cMonitor, destroyers),
|
||||
func(user string) (operator.QueryFunc, error) {
|
||||
query, _, err := client.AdaptFunc(
|
||||
cMonitor,
|
||||
namespace,
|
||||
componentLabels,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queryClient := query(user)
|
||||
|
||||
return func(k8sClient kubernetes.ClientInt, queried map[string]interface{}) (operator.EnsureFunc, error) {
|
||||
_, err := queryNode(k8sClient, queried)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return queryClient(k8sClient, queried)
|
||||
}, nil
|
||||
},
|
||||
func(user string) (operator.DestroyFunc, error) {
|
||||
_, destroy, err := client.AdaptFunc(
|
||||
cMonitor,
|
||||
namespace,
|
||||
componentLabels,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return destroy(user), nil
|
||||
},
|
||||
func(k8sClient kubernetes.ClientInt) ([]string, error) {
|
||||
return client.QueryCertificates(namespace, labels.DeriveComponentSelector(componentLabels, false), k8sClient)
|
||||
},
|
||||
nil
|
||||
}
|
@ -0,0 +1,305 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
kubernetesmock "github.com/caos/orbos/pkg/kubernetes/mock"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/core"
|
||||
coremock "github.com/caos/zitadel/operator/database/kinds/databases/core/mock"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/managed/certificate/pem"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
caPem = `-----BEGIN CERTIFICATE-----
|
||||
MIIE9TCCAt2gAwIBAgICB+MwDQYJKoZIhvcNAQELBQAwKzESMBAGA1UEChMJQ29j
|
||||
a3JvYWNoMRUwEwYDVQQDEwxDb2Nrcm9hY2ggQ0EwHhcNMjAxMjAxMTAyMjA0WhcN
|
||||
MzAxMjAxMTAyMjA0WjArMRIwEAYDVQQKEwlDb2Nrcm9hY2gxFTATBgNVBAMTDENv
|
||||
Y2tyb2FjaCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOja5IXJ
|
||||
GUY9sFgyvWkav+O74gzcv8y69uJzSOx0piOP+sfpZWVeGEjqO4JgdcS5NPMrT5Tb
|
||||
aJ52CjiOdlHVTyR87i5JmOIvA2qA4dWQmSbX7AQ8r9ptYDe9xMn+qFegcbr4YxCz
|
||||
K9mmbDZUhlLO7cz3QV6nvRxGFWbzffo8BXZnOUCAOyOHrbnPpLumnfZlL5BckdtY
|
||||
pS7jAlUpKSMBTK4AHcmrouFsNKHqlUopYXeJFdg9g1F0DuCnVP9x7+XcUW8dAVut
|
||||
Q7Jswy+++GAXs6mPVsYFLXUSYNyW+Bfl/jKwx0XTQx/6iyNpK0XtAzjBZFjXwmot
|
||||
0mODkqnfE3BB4lXxZ5knomAQEGSScUhCUb9upbF4uJJF27xr/kIkwtWxMGpCXds0
|
||||
IxI+wNRCenhfFZEIQCzri0zn6WdN8b/gbv1BErNcccYwolPUv1oUgYbzowbQ5O2D
|
||||
aLQPqO1VAZiZHLxb787bRywpCl33VZ1ptMHi2ogKjcsh4DQ9SsRj+rU/Tk5lyk7G
|
||||
FHteyHcq12TGpz9/CQYMacl8yeRRHfNO3Rq0jFTYeD4+ZdVBPKeuTHyXGzy2T157
|
||||
pgqMFzwqxlNYPzpuz7xsZBExJzCtcomB8fMlsCJnxV/kuMTTsWsPrRc7hsqzBCq3
|
||||
plfYT8a7EBCVmJmDu+6mMVh+A9M2zZCqtV57AgMBAAGjIzAhMA4GA1UdDwEB/wQE
|
||||
AwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQDRmkBYDk/F
|
||||
lYTgMaL7yhRAowDR8mtOl06Oba/MCDDsmBvfOVIqY9Sa9sJtb78Uvecv9WAE4qpb
|
||||
PNSaIvNmujmGQrBxtGwQdeHZvtXQ1lKAGBM+ukfz/NFU+6rwOdTuLgS5jTaiM4r1
|
||||
uMoIG6S5z/brWGHAw8Qv2Fq05dSLnevmPWK903V/RnwmCKT9as5Qpfkaie2PNory
|
||||
euxVGeGolxzgbSps3ZJlDSSymQBl10iJYYdGIsgcw5FHcCdpS4AutdpQbIFuCGk2
|
||||
CHTcTTa4KMaVfRm/gKm1gh8UnuVDLBKQS8+1CHFYUnih8ozBNUyBo5r5L4BHJK/n
|
||||
f0gnrYqaxtqPAyHkfo0PRc50HlAQRS/gW8ESv2PQmcSWlDggEzXt5MqFIYOfEXWE
|
||||
gtC7Ct1P7gzIRxolYVsNSgBR62sJM1PUa39E5v0QJsmuM51txHuw/PGqkkBlEwx9
|
||||
4u3FFXD9Pslg/g1l50Ek5O3/TWMta4Q+NagDDaOdkb4LRQr522ySgGIinBw00Xr+
|
||||
Ics2L42rP1LQyaE7+BljDEwmGaT7mrl8QGZR9uv/9mbtk2+q7wAOQzcMSFFACfwx
|
||||
KdKENmK0GpQZ6HaeDZO7wxGiAyFKhTNpd39XNOEhaKpWLSUxnZTLGlMIzvSyMGCj
|
||||
PV3xm+Xg1Xvki7f/qC1djysMcaokoJzNdg==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
caPrivPem = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJKAIBAAKCAgEA2Owc3IOljq3hkKO9q6jT1kFZp/+Y9dv/lTIbsMhV7gPrn2WS
|
||||
Os2KQphcrB9DvGoZAxUh1aD8MUO7USIMhFodQEq7vfycDT16jrTnc1fSDDcfk3Ra
|
||||
DvqZGcBkj0lc6w5LK3FNl2y6rA/26teGbyNaVXJfW01dKKy4E2or2+1+tUamyjiU
|
||||
cvPzNWEoPPJT5HcadLTxZr/SKDvDXFyej4nKT9+j1pKmaqQrIrL4KXKq75LhU+kH
|
||||
L4TG+kJ35isiv5OTpd2jG6ssz0i+ZEtX4hjIM6eCnZaiFT//33nSL3zl2LUjmyou
|
||||
ks2FDuX9m90mg8UtcrpA/eVlwyg8nJ/d7/Yn3ZjuHVOgzE8YuQXauJ16HuLcFgEW
|
||||
WQg1uwhOhrc5b0YJndZbJ2Re4qfaBoC5fODRXoUPvqG9k9kNp98xtclNCIyW8EpA
|
||||
Su8QmK42ksOJox+OQamHzatrGppgIK77TY3ZFcBHETHClabgsfjv/1GNXXAexJI4
|
||||
Cjntnou+yoc+LUj87WJfD3ERGgm0nDfnE7uZ7kccO5Lj/oajeO+Q+QJaLZ4I2jz+
|
||||
a05k16naGGT29AnM+iwBqqoTODr4Z7905niZc6+fEOPml1V7wSuJs2eE4jOa3ixX
|
||||
5tnruw74rN82Zfrkg6kWPOEfBBXzSotRiHv+BAV2tFbnC55ItnHn1ZE68V0CAwEA
|
||||
AQKCAgEAssWsZ4PLXpIo8qYve5hQtSP4er7oVb8wnMnGDmSchOMQPbZc1D9uscGV
|
||||
pnjBvzcFVAgHcWMSVJuIda4E+NK3hrPQlBvqk/LV3WRz1xhKUKzhRgm+6tdWc+We
|
||||
OoRwonuOMchX9PKzyXgCu7pR3agaG499zOYuX4Yw0jdO3BqXsVf/v2rv1Oj9yEFB
|
||||
AzGHOCN8VzCEPnTaAzR1pdnjB1K8vCUIhp8nrX2M2zT51lbdT0ISl6/VrzDTN46t
|
||||
97AXHCHIrgrCENx6un4uAsQhMoHQBNoJiEyLWc371zYzpdVeK8HlDUyvQ2dDQGsF
|
||||
Hn4c7r4C3aloRJbYzgSMJ1yNcOTCJpciQsq1VmCQFOHfbum2ejquXJ7BbeRGeHdM
|
||||
145epckL+hbECTCpSs0Z5t90NdfoJspvr+3sOEt6h3DMUGjvobrf/s3KiRY5hHdc
|
||||
x86Htx3QgWNCG6a+ga7h7s3t+4ZtoPPWn+vlAoxD/2eCzsDgE/XLDmC2T4yS8UOb
|
||||
LIb4UN7wl2sNM8zhm4BfoiKfjGu4dPZIlsPP+ZKRby1O4ogHHoPREBTH1VSEplVM
|
||||
fA/KSITV+rUfO3T/qXIFZ4/Wa5YZoULiMCOJOWNgXQzWvTf7Qr31LhhfXd+uIw30
|
||||
LDtjdkpT43zlKxRosQFLiV7q3fVbvKPVQfxzBz7M1Gl74IllpmECggEBAOnAimKl
|
||||
w/9mcMkEd2L2ZzDI+57W8DU6grUUILZfjJG+uvOiMmIA5M1wzsVHil3ihZthrJ5A
|
||||
UUAKG97JgwjpIAliAzgtPZY2AK/bJz7Hht1EwewrV+RyHM+dMv6em2OA+XUjFSPX
|
||||
VsecFDaUQebkypw236KdA4jgRG6hr4xObdXgTN6RFCv1mI1EijZ/YbKgPgTJaPI4
|
||||
b2N5QokYFygUCwRxKIt7/Z4hQs9LbdW680NcXtPRPnS1SmwYJbi7wTX+o5f6nfNV
|
||||
YvojborjXwNrZe0p+FfaEuD44wf6kNlRGfcKXoaAncXV/M5/oXf1dktKP630eq9g
|
||||
0MAKFYJ6MAUheakCggEBAO2Rgfy/tEydBuP9BIfC84BXk8EwnRJ0ZfmXv77HFj3F
|
||||
VT5QNU97I5gZJOFkL7OO8bL0/WljlqeBEht0DmHNmOt5Pyew4LRLpfeN6rClZgRN
|
||||
V4wqKXjoZAIYa9khQcwwFNER1RdI+PkuusJtrvY6CbwwG9LbBq2NR4C1YSgaQnhV
|
||||
NqdXK5dwrYEky6lI31sDD4BYeiJVKlkkNCQAVOC+04Mrsa9F0NG7TKXzji5hU8l5
|
||||
x8squjvJ6vmobhmsRTL1LMpafUrt5pHL9jcWIZYxJJo9mB0zxJKcsLI8IOg2QPoj
|
||||
tQ395FZ2YtjNzZa1CYeUOUiaQu+uvztfE36AdW/vUpUCggEAMV7bW66LURw/4hUx
|
||||
ahOFBAbPLmNTZMqw5LIVnq9br0TLk73ESnLJ4KJc6coMbXv0oDbnEJ2hC5eW/10s
|
||||
cetbOuAasfjMMzfAuWPeTCI0V/O3ybv12mhHsYoQRTsWstOA3L7GLkXDLHHIyyZR
|
||||
LQVRzeDBJ0Vmg7hqe7tmqom+JRg05CVcT1SWHfBGCPCqn+G8d6Jaqh5FWIs6BF60
|
||||
NWDWWt/TonJTxNxdkg7qaeQMkUOnO7HMMTZBO8d14Ci3zEG2J9llFwoH17E4Hdmc
|
||||
Lcq3QnpE27lRl3a57Ot9QIkipMzp3hq4OBrURIEsh3uuuoQ6IvGqH/Sg4o6+sEpC
|
||||
bjL90QKCAQBDc/0kdooK9sruEPkoUwIwfq1FPThb9RC/PYcD9CMshssdVkjMuHny
|
||||
xbDjDj89DGk0FrudINm11b/+a4Vp36Z7tYFpE5+5kYEeOP1aCpxcvFkPQyljWxiK
|
||||
P8TfccHs5/oBIr8OTXnjxpDgg6QZ5YC+HirIQ8gxntuef+GGMW6OHCPYf7ew2B1r
|
||||
fbcV6csBXG0aVATZmrTbepwTXMS8y3Hi3JUm3vvbkQLCW9US9i+EFT/VP9yA/WPq
|
||||
Xxhj0bYUMej1y5unmsTMwMy392Cx9GIgKTz3jatStYq2ELyHMmBgpaLSxjP/GL4Y
|
||||
MNce42hBRqS9KI+43jUN9oDiejbeAWXBAoIBACZKnS6EppNDSdos0f32J5TxEUxv
|
||||
lF9AuVXEAPDR/m3+PlSRzrlf7uowdHTfsemHMSduL8weLNhduprwz4TmW9Fo6fSF
|
||||
UePLNbXcMX3omAk+AKKOiexLG0fCXGW2zr4nHZJzTbK2+La3yLeUcoPu1puNpLiq
|
||||
LVj2bH3zKWVRA9/ovuN6V5w18ojjdqOw4bw5qXcdZhoWLxI8Q9Oqua24f/dnRpuI
|
||||
I8mRtPQ3+vuOKbTT+/80eAUpSfEKwAg1Mjgury9q1/B4Ib6hAGzpJuXxG7xQjnsJ
|
||||
EFcN1kvdg5WGK41+fYMdexPaLamjhDGN0e1vxJfAukWIAsBMwp8wfEWZvzA=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`
|
||||
)
|
||||
|
||||
func TestCertificate_AdaptWithCA(t *testing.T) {
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs"
|
||||
clusterDns := "testDns"
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "cockroachdb", "v0"), "cockroachdb")
|
||||
|
||||
nodeLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "cockroachdb",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": "cockroachdb.node",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"orbos.ch/selectable": "yes",
|
||||
}
|
||||
dbCurrent := coremock.NewMockDatabaseCurrent(gomock.NewController(t))
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
secretList := &corev1.SecretList{
|
||||
Items: []corev1.Secret{},
|
||||
}
|
||||
ca, err := pem.DecodeCertificate([]byte(caPem))
|
||||
assert.NoError(t, err)
|
||||
caPriv, err := pem.DecodeKey([]byte(caPrivPem))
|
||||
assert.NoError(t, err)
|
||||
|
||||
k8sClient.EXPECT().ListSecrets(namespace, nodeLabels).Times(1).Return(secretList, nil)
|
||||
dbCurrent.EXPECT().GetCertificate().Times(1).Return(ca)
|
||||
dbCurrent.EXPECT().GetCertificateKey().Times(1).Return(caPriv)
|
||||
dbCurrent.EXPECT().SetCertificate(ca).Times(1)
|
||||
dbCurrent.EXPECT().SetCertificateKey(caPriv).Times(1)
|
||||
|
||||
queried := map[string]interface{}{}
|
||||
current := &tree.Tree{
|
||||
Parsed: dbCurrent,
|
||||
}
|
||||
|
||||
core.SetQueriedForDatabase(queried, current)
|
||||
|
||||
query, _, _, _, _, err := AdaptFunc(monitor, namespace, componentLabels, clusterDns, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
ensure, err := query(k8sClient, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ensure)
|
||||
|
||||
assert.NoError(t, ensure(k8sClient))
|
||||
}
|
||||
|
||||
func TestNode_AdaptWithoutCA(t *testing.T) {
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs"
|
||||
clusterDns := "testDns"
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "cockroachdb", "v0"), "cockroachdb")
|
||||
|
||||
nodeLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "cockroachdb",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": "cockroachdb.node",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"orbos.ch/selectable": "yes",
|
||||
}
|
||||
dbCurrent := coremock.NewMockDatabaseCurrent(gomock.NewController(t))
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
secretList := &corev1.SecretList{
|
||||
Items: []corev1.Secret{},
|
||||
}
|
||||
|
||||
k8sClient.EXPECT().ListSecrets(namespace, nodeLabels).Times(1).Return(secretList, nil)
|
||||
dbCurrent.EXPECT().GetCertificate().Times(1).Return(nil)
|
||||
dbCurrent.EXPECT().GetCertificateKey().Times(1).Return(nil)
|
||||
dbCurrent.EXPECT().SetCertificate(gomock.Any()).Times(1)
|
||||
dbCurrent.EXPECT().SetCertificateKey(gomock.Any()).Times(1)
|
||||
k8sClient.EXPECT().ApplySecret(gomock.Any()).Times(1).Return(nil)
|
||||
|
||||
queried := map[string]interface{}{}
|
||||
current := &tree.Tree{
|
||||
Parsed: dbCurrent,
|
||||
}
|
||||
|
||||
core.SetQueriedForDatabase(queried, current)
|
||||
|
||||
query, _, _, _, _, err := AdaptFunc(monitor, namespace, componentLabels, clusterDns, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
ensure, err := query(k8sClient, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ensure)
|
||||
|
||||
assert.NoError(t, ensure(k8sClient))
|
||||
}
|
||||
|
||||
func TestNode_AdaptAlreadyExisting(t *testing.T) {
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs"
|
||||
clusterDns := "testDns"
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "cockroachdb", "v0"), "cockroachdb")
|
||||
|
||||
nodeLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "cockroachdb",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": "cockroachdb.node",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"orbos.ch/selectable": "yes",
|
||||
}
|
||||
dbCurrent := coremock.NewMockDatabaseCurrent(gomock.NewController(t))
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
caCertKey := "ca.crt"
|
||||
caPrivKeyKey := "ca.key"
|
||||
|
||||
secretList := &corev1.SecretList{
|
||||
Items: []corev1.Secret{{
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Data: map[string][]byte{
|
||||
caCertKey: []byte(caPem),
|
||||
caPrivKeyKey: []byte(caPrivPem),
|
||||
},
|
||||
Type: "Opaque",
|
||||
}},
|
||||
}
|
||||
|
||||
k8sClient.EXPECT().ListSecrets(namespace, nodeLabels).Times(1).Return(secretList, nil)
|
||||
dbCurrent.EXPECT().SetCertificate(gomock.Any()).Times(1)
|
||||
dbCurrent.EXPECT().SetCertificateKey(gomock.Any()).Times(1)
|
||||
|
||||
queried := map[string]interface{}{}
|
||||
current := &tree.Tree{
|
||||
Parsed: dbCurrent,
|
||||
}
|
||||
|
||||
core.SetQueriedForDatabase(queried, current)
|
||||
|
||||
query, _, _, _, _, err := AdaptFunc(monitor, namespace, componentLabels, clusterDns, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
ensure, err := query(k8sClient, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ensure)
|
||||
|
||||
assert.NoError(t, ensure(k8sClient))
|
||||
}
|
||||
|
||||
func TestNode_AdaptCreateUser(t *testing.T) {
|
||||
monitor := mntr.Monitor{}
|
||||
clusterDns := "testDns"
|
||||
namespace := "testNs"
|
||||
user := "test"
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "cockroachdb", "v0"), "cockroachdb")
|
||||
|
||||
nodeLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "cockroachdb",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/name": "cockroachdb.node",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"orbos.ch/selectable": "yes",
|
||||
}
|
||||
dbCurrent := coremock.NewMockDatabaseCurrent(gomock.NewController(t))
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
ca, err := pem.DecodeCertificate([]byte(caPem))
|
||||
assert.NoError(t, err)
|
||||
caPriv, err := pem.DecodeKey([]byte(caPrivPem))
|
||||
assert.NoError(t, err)
|
||||
|
||||
caCertKey := "ca.crt"
|
||||
caPrivKeyKey := "ca.key"
|
||||
|
||||
secretList := &corev1.SecretList{
|
||||
Items: []corev1.Secret{{
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Data: map[string][]byte{
|
||||
caCertKey: []byte(caPem),
|
||||
caPrivKeyKey: []byte(caPrivPem),
|
||||
},
|
||||
Type: "Opaque",
|
||||
}},
|
||||
}
|
||||
|
||||
k8sClient.EXPECT().ListSecrets(namespace, nodeLabels).Times(1).Return(secretList, nil)
|
||||
|
||||
dbCurrent.EXPECT().GetCertificate().Times(1).Return(ca)
|
||||
dbCurrent.EXPECT().GetCertificateKey().Times(1).Return(caPriv)
|
||||
dbCurrent.EXPECT().SetCertificate(gomock.Any()).Times(1)
|
||||
dbCurrent.EXPECT().SetCertificateKey(gomock.Any()).Times(1)
|
||||
k8sClient.EXPECT().ApplySecret(gomock.Any())
|
||||
|
||||
queried := map[string]interface{}{}
|
||||
current := &tree.Tree{
|
||||
Parsed: dbCurrent,
|
||||
}
|
||||
core.SetQueriedForDatabase(queried, current)
|
||||
|
||||
_, _, createUser, _, _, err := AdaptFunc(monitor, namespace, componentLabels, clusterDns, true)
|
||||
assert.NoError(t, err)
|
||||
query, err := createUser(user)
|
||||
assert.NoError(t, err)
|
||||
|
||||
ensure, err := query(k8sClient, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ensure)
|
||||
|
||||
assert.NoError(t, ensure(k8sClient))
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package certificates
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"math/big"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewCA() (*rsa.PrivateKey, []byte, error) {
|
||||
ca := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(2019),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Cockroach"},
|
||||
CommonName: "Cockroach CA",
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
IsCA: true,
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
caPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return caPrivKey, caBytes, nil
|
||||
}
|
||||
|
||||
func NewClient(caPrivKey *rsa.PrivateKey, ca []byte, user string) (*rsa.PrivateKey, []byte, error) {
|
||||
cert := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1658),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Cockroach"},
|
||||
CommonName: user,
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
}
|
||||
|
||||
certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
caCert, err := x509.ParseCertificate(ca)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, cert, caCert, &certPrivKey.PublicKey, caPrivKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return certPrivKey, certBytes, nil
|
||||
}
|
||||
|
||||
func NewNode(caPrivKey *rsa.PrivateKey, ca []byte, namespace string, clusterDns string) (*rsa.PrivateKey, []byte, error) {
|
||||
cert := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1658),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Cockroach"},
|
||||
CommonName: "node",
|
||||
},
|
||||
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1)},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(10, 0, 0),
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
DNSNames: []string{
|
||||
"localhost",
|
||||
"cockroachdb-public",
|
||||
"cockroachdb-public.default",
|
||||
"cockroachdb-public." + namespace,
|
||||
"cockroachdb-public." + namespace + ".svc." + clusterDns,
|
||||
"*.cockroachdb",
|
||||
"*.cockroachdb." + namespace,
|
||||
"*.cockroachdb." + namespace + ".svc." + clusterDns,
|
||||
},
|
||||
}
|
||||
|
||||
certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
caCert, err := x509.ParseCertificate(ca)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, cert, caCert, &certPrivKey.PublicKey, caPrivKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return certPrivKey, certBytes, nil
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package certificates
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/managed/certificate/pem"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCertificates_CAE(t *testing.T) {
|
||||
priv, rootCa, err := NewCA()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, priv)
|
||||
|
||||
pemCa, err := pem.EncodeCertificate(rootCa)
|
||||
pemkey, err := pem.EncodeKey(priv)
|
||||
assert.NotNil(t, pemCa)
|
||||
assert.NotNil(t, pemkey)
|
||||
|
||||
_, err = x509.ParseCertificate(rootCa)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCertificates_CA(t *testing.T) {
|
||||
_, rootCa, err := NewCA()
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = x509.ParseCertificate(rootCa)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCertificates_Chain(t *testing.T) {
|
||||
rootKey, rootCert, err := NewCA()
|
||||
assert.NoError(t, err)
|
||||
rootPem, err := pem.EncodeCertificate(rootCert)
|
||||
assert.NoError(t, err)
|
||||
|
||||
roots := x509.NewCertPool()
|
||||
ok := roots.AppendCertsFromPEM(rootPem)
|
||||
assert.Equal(t, ok, true)
|
||||
|
||||
_, clientCert, err := NewClient(rootKey, rootCert, "test")
|
||||
|
||||
cert, err := x509.ParseCertificate(clientCert)
|
||||
assert.NoError(t, err)
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: roots,
|
||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
|
||||
_, err = cert.Verify(opts)
|
||||
assert.NoError(t, err)
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/caos/zitadel/operator"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
|
||||
"github.com/caos/orbos/mntr"
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
"github.com/caos/orbos/pkg/kubernetes/resources/secret"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/core"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/managed/certificate/certificates"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/managed/certificate/pem"
|
||||
)
|
||||
|
||||
const (
|
||||
clientSecretPrefix = "cockroachdb.client."
|
||||
caCertKey = "ca.crt"
|
||||
clientCertKeyPrefix = "client."
|
||||
clientCertKeySuffix = ".crt"
|
||||
clientPrivKeyKeyPrefix = "client."
|
||||
clientPrivKeyKeySuffix = ".key"
|
||||
)
|
||||
|
||||
func AdaptFunc(
|
||||
monitor mntr.Monitor,
|
||||
namespace string,
|
||||
componentLabels *labels.Component,
|
||||
) (
|
||||
func(client string) operator.QueryFunc,
|
||||
func(client string) operator.DestroyFunc,
|
||||
error,
|
||||
) {
|
||||
|
||||
return func(client string) operator.QueryFunc {
|
||||
clientSecret := clientSecretPrefix + client
|
||||
nameLabels := labels.MustForName(componentLabels, strings.ReplaceAll(clientSecret, "_", "-"))
|
||||
clientCertKey := clientCertKeyPrefix + client + clientCertKeySuffix
|
||||
clientPrivKeyKey := clientPrivKeyKeyPrefix + client + clientPrivKeyKeySuffix
|
||||
|
||||
return func(k8sClient kubernetes.ClientInt, queried map[string]interface{}) (operator.EnsureFunc, error) {
|
||||
queriers := make([]operator.QueryFunc, 0)
|
||||
|
||||
currentDB, err := core.ParseQueriedForDatabase(queried)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
caCert := currentDB.GetCertificate()
|
||||
caKey := currentDB.GetCertificateKey()
|
||||
if caKey == nil || caCert == nil || len(caCert) == 0 {
|
||||
return nil, errors.New("no ca-certificate found")
|
||||
}
|
||||
|
||||
clientPrivKey, clientCert, err := certificates.NewClient(caKey, caCert, client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pemClientPrivKey, err := pem.EncodeKey(clientPrivKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pemClientCert, err := pem.EncodeCertificate(clientCert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pemCaCert, err := pem.EncodeCertificate(caCert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientSecretData := map[string]string{
|
||||
caCertKey: string(pemCaCert),
|
||||
clientPrivKeyKey: string(pemClientPrivKey),
|
||||
clientCertKey: string(pemClientCert),
|
||||
}
|
||||
|
||||
queryClientSecret, err := secret.AdaptFuncToEnsure(namespace, labels.AsSelectable(nameLabels), clientSecretData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queriers = append(queriers, operator.ResourceQueryToZitadelQuery(queryClientSecret))
|
||||
|
||||
return operator.QueriersToEnsureFunc(monitor, false, queriers, k8sClient, queried)
|
||||
}
|
||||
}, func(client string) operator.DestroyFunc {
|
||||
clientSecret := clientSecretPrefix + client
|
||||
|
||||
destroy, err := secret.AdaptFuncToDestroy(namespace, clientSecret)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return operator.ResourceDestroyToZitadelDestroy(destroy)
|
||||
},
|
||||
nil
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/caos/orbos/mntr"
|
||||
kubernetesmock "github.com/caos/orbos/pkg/kubernetes/mock"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/caos/orbos/pkg/tree"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/core"
|
||||
coremock "github.com/caos/zitadel/operator/database/kinds/databases/core/mock"
|
||||
"github.com/caos/zitadel/operator/database/kinds/databases/managed/certificate/pem"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
caPem = `-----BEGIN CERTIFICATE-----
|
||||
MIIE9TCCAt2gAwIBAgICB+MwDQYJKoZIhvcNAQELBQAwKzESMBAGA1UEChMJQ29j
|
||||
a3JvYWNoMRUwEwYDVQQDEwxDb2Nrcm9hY2ggQ0EwHhcNMjAxMjAxMTAyMjA0WhcN
|
||||
MzAxMjAxMTAyMjA0WjArMRIwEAYDVQQKEwlDb2Nrcm9hY2gxFTATBgNVBAMTDENv
|
||||
Y2tyb2FjaCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOja5IXJ
|
||||
GUY9sFgyvWkav+O74gzcv8y69uJzSOx0piOP+sfpZWVeGEjqO4JgdcS5NPMrT5Tb
|
||||
aJ52CjiOdlHVTyR87i5JmOIvA2qA4dWQmSbX7AQ8r9ptYDe9xMn+qFegcbr4YxCz
|
||||
K9mmbDZUhlLO7cz3QV6nvRxGFWbzffo8BXZnOUCAOyOHrbnPpLumnfZlL5BckdtY
|
||||
pS7jAlUpKSMBTK4AHcmrouFsNKHqlUopYXeJFdg9g1F0DuCnVP9x7+XcUW8dAVut
|
||||
Q7Jswy+++GAXs6mPVsYFLXUSYNyW+Bfl/jKwx0XTQx/6iyNpK0XtAzjBZFjXwmot
|
||||
0mODkqnfE3BB4lXxZ5knomAQEGSScUhCUb9upbF4uJJF27xr/kIkwtWxMGpCXds0
|
||||
IxI+wNRCenhfFZEIQCzri0zn6WdN8b/gbv1BErNcccYwolPUv1oUgYbzowbQ5O2D
|
||||
aLQPqO1VAZiZHLxb787bRywpCl33VZ1ptMHi2ogKjcsh4DQ9SsRj+rU/Tk5lyk7G
|
||||
FHteyHcq12TGpz9/CQYMacl8yeRRHfNO3Rq0jFTYeD4+ZdVBPKeuTHyXGzy2T157
|
||||
pgqMFzwqxlNYPzpuz7xsZBExJzCtcomB8fMlsCJnxV/kuMTTsWsPrRc7hsqzBCq3
|
||||
plfYT8a7EBCVmJmDu+6mMVh+A9M2zZCqtV57AgMBAAGjIzAhMA4GA1UdDwEB/wQE
|
||||
AwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQDRmkBYDk/F
|
||||
lYTgMaL7yhRAowDR8mtOl06Oba/MCDDsmBvfOVIqY9Sa9sJtb78Uvecv9WAE4qpb
|
||||
PNSaIvNmujmGQrBxtGwQdeHZvtXQ1lKAGBM+ukfz/NFU+6rwOdTuLgS5jTaiM4r1
|
||||
uMoIG6S5z/brWGHAw8Qv2Fq05dSLnevmPWK903V/RnwmCKT9as5Qpfkaie2PNory
|
||||
euxVGeGolxzgbSps3ZJlDSSymQBl10iJYYdGIsgcw5FHcCdpS4AutdpQbIFuCGk2
|
||||
CHTcTTa4KMaVfRm/gKm1gh8UnuVDLBKQS8+1CHFYUnih8ozBNUyBo5r5L4BHJK/n
|
||||
f0gnrYqaxtqPAyHkfo0PRc50HlAQRS/gW8ESv2PQmcSWlDggEzXt5MqFIYOfEXWE
|
||||
gtC7Ct1P7gzIRxolYVsNSgBR62sJM1PUa39E5v0QJsmuM51txHuw/PGqkkBlEwx9
|
||||
4u3FFXD9Pslg/g1l50Ek5O3/TWMta4Q+NagDDaOdkb4LRQr522ySgGIinBw00Xr+
|
||||
Ics2L42rP1LQyaE7+BljDEwmGaT7mrl8QGZR9uv/9mbtk2+q7wAOQzcMSFFACfwx
|
||||
KdKENmK0GpQZ6HaeDZO7wxGiAyFKhTNpd39XNOEhaKpWLSUxnZTLGlMIzvSyMGCj
|
||||
PV3xm+Xg1Xvki7f/qC1djysMcaokoJzNdg==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
caPrivPem = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJKAIBAAKCAgEA2Owc3IOljq3hkKO9q6jT1kFZp/+Y9dv/lTIbsMhV7gPrn2WS
|
||||
Os2KQphcrB9DvGoZAxUh1aD8MUO7USIMhFodQEq7vfycDT16jrTnc1fSDDcfk3Ra
|
||||
DvqZGcBkj0lc6w5LK3FNl2y6rA/26teGbyNaVXJfW01dKKy4E2or2+1+tUamyjiU
|
||||
cvPzNWEoPPJT5HcadLTxZr/SKDvDXFyej4nKT9+j1pKmaqQrIrL4KXKq75LhU+kH
|
||||
L4TG+kJ35isiv5OTpd2jG6ssz0i+ZEtX4hjIM6eCnZaiFT//33nSL3zl2LUjmyou
|
||||
ks2FDuX9m90mg8UtcrpA/eVlwyg8nJ/d7/Yn3ZjuHVOgzE8YuQXauJ16HuLcFgEW
|
||||
WQg1uwhOhrc5b0YJndZbJ2Re4qfaBoC5fODRXoUPvqG9k9kNp98xtclNCIyW8EpA
|
||||
Su8QmK42ksOJox+OQamHzatrGppgIK77TY3ZFcBHETHClabgsfjv/1GNXXAexJI4
|
||||
Cjntnou+yoc+LUj87WJfD3ERGgm0nDfnE7uZ7kccO5Lj/oajeO+Q+QJaLZ4I2jz+
|
||||
a05k16naGGT29AnM+iwBqqoTODr4Z7905niZc6+fEOPml1V7wSuJs2eE4jOa3ixX
|
||||
5tnruw74rN82Zfrkg6kWPOEfBBXzSotRiHv+BAV2tFbnC55ItnHn1ZE68V0CAwEA
|
||||
AQKCAgEAssWsZ4PLXpIo8qYve5hQtSP4er7oVb8wnMnGDmSchOMQPbZc1D9uscGV
|
||||
pnjBvzcFVAgHcWMSVJuIda4E+NK3hrPQlBvqk/LV3WRz1xhKUKzhRgm+6tdWc+We
|
||||
OoRwonuOMchX9PKzyXgCu7pR3agaG499zOYuX4Yw0jdO3BqXsVf/v2rv1Oj9yEFB
|
||||
AzGHOCN8VzCEPnTaAzR1pdnjB1K8vCUIhp8nrX2M2zT51lbdT0ISl6/VrzDTN46t
|
||||
97AXHCHIrgrCENx6un4uAsQhMoHQBNoJiEyLWc371zYzpdVeK8HlDUyvQ2dDQGsF
|
||||
Hn4c7r4C3aloRJbYzgSMJ1yNcOTCJpciQsq1VmCQFOHfbum2ejquXJ7BbeRGeHdM
|
||||
145epckL+hbECTCpSs0Z5t90NdfoJspvr+3sOEt6h3DMUGjvobrf/s3KiRY5hHdc
|
||||
x86Htx3QgWNCG6a+ga7h7s3t+4ZtoPPWn+vlAoxD/2eCzsDgE/XLDmC2T4yS8UOb
|
||||
LIb4UN7wl2sNM8zhm4BfoiKfjGu4dPZIlsPP+ZKRby1O4ogHHoPREBTH1VSEplVM
|
||||
fA/KSITV+rUfO3T/qXIFZ4/Wa5YZoULiMCOJOWNgXQzWvTf7Qr31LhhfXd+uIw30
|
||||
LDtjdkpT43zlKxRosQFLiV7q3fVbvKPVQfxzBz7M1Gl74IllpmECggEBAOnAimKl
|
||||
w/9mcMkEd2L2ZzDI+57W8DU6grUUILZfjJG+uvOiMmIA5M1wzsVHil3ihZthrJ5A
|
||||
UUAKG97JgwjpIAliAzgtPZY2AK/bJz7Hht1EwewrV+RyHM+dMv6em2OA+XUjFSPX
|
||||
VsecFDaUQebkypw236KdA4jgRG6hr4xObdXgTN6RFCv1mI1EijZ/YbKgPgTJaPI4
|
||||
b2N5QokYFygUCwRxKIt7/Z4hQs9LbdW680NcXtPRPnS1SmwYJbi7wTX+o5f6nfNV
|
||||
YvojborjXwNrZe0p+FfaEuD44wf6kNlRGfcKXoaAncXV/M5/oXf1dktKP630eq9g
|
||||
0MAKFYJ6MAUheakCggEBAO2Rgfy/tEydBuP9BIfC84BXk8EwnRJ0ZfmXv77HFj3F
|
||||
VT5QNU97I5gZJOFkL7OO8bL0/WljlqeBEht0DmHNmOt5Pyew4LRLpfeN6rClZgRN
|
||||
V4wqKXjoZAIYa9khQcwwFNER1RdI+PkuusJtrvY6CbwwG9LbBq2NR4C1YSgaQnhV
|
||||
NqdXK5dwrYEky6lI31sDD4BYeiJVKlkkNCQAVOC+04Mrsa9F0NG7TKXzji5hU8l5
|
||||
x8squjvJ6vmobhmsRTL1LMpafUrt5pHL9jcWIZYxJJo9mB0zxJKcsLI8IOg2QPoj
|
||||
tQ395FZ2YtjNzZa1CYeUOUiaQu+uvztfE36AdW/vUpUCggEAMV7bW66LURw/4hUx
|
||||
ahOFBAbPLmNTZMqw5LIVnq9br0TLk73ESnLJ4KJc6coMbXv0oDbnEJ2hC5eW/10s
|
||||
cetbOuAasfjMMzfAuWPeTCI0V/O3ybv12mhHsYoQRTsWstOA3L7GLkXDLHHIyyZR
|
||||
LQVRzeDBJ0Vmg7hqe7tmqom+JRg05CVcT1SWHfBGCPCqn+G8d6Jaqh5FWIs6BF60
|
||||
NWDWWt/TonJTxNxdkg7qaeQMkUOnO7HMMTZBO8d14Ci3zEG2J9llFwoH17E4Hdmc
|
||||
Lcq3QnpE27lRl3a57Ot9QIkipMzp3hq4OBrURIEsh3uuuoQ6IvGqH/Sg4o6+sEpC
|
||||
bjL90QKCAQBDc/0kdooK9sruEPkoUwIwfq1FPThb9RC/PYcD9CMshssdVkjMuHny
|
||||
xbDjDj89DGk0FrudINm11b/+a4Vp36Z7tYFpE5+5kYEeOP1aCpxcvFkPQyljWxiK
|
||||
P8TfccHs5/oBIr8OTXnjxpDgg6QZ5YC+HirIQ8gxntuef+GGMW6OHCPYf7ew2B1r
|
||||
fbcV6csBXG0aVATZmrTbepwTXMS8y3Hi3JUm3vvbkQLCW9US9i+EFT/VP9yA/WPq
|
||||
Xxhj0bYUMej1y5unmsTMwMy392Cx9GIgKTz3jatStYq2ELyHMmBgpaLSxjP/GL4Y
|
||||
MNce42hBRqS9KI+43jUN9oDiejbeAWXBAoIBACZKnS6EppNDSdos0f32J5TxEUxv
|
||||
lF9AuVXEAPDR/m3+PlSRzrlf7uowdHTfsemHMSduL8weLNhduprwz4TmW9Fo6fSF
|
||||
UePLNbXcMX3omAk+AKKOiexLG0fCXGW2zr4nHZJzTbK2+La3yLeUcoPu1puNpLiq
|
||||
LVj2bH3zKWVRA9/ovuN6V5w18ojjdqOw4bw5qXcdZhoWLxI8Q9Oqua24f/dnRpuI
|
||||
I8mRtPQ3+vuOKbTT+/80eAUpSfEKwAg1Mjgury9q1/B4Ib6hAGzpJuXxG7xQjnsJ
|
||||
EFcN1kvdg5WGK41+fYMdexPaLamjhDGN0e1vxJfAukWIAsBMwp8wfEWZvzA=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`
|
||||
)
|
||||
|
||||
func TestClient_Adapt1(t *testing.T) {
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs"
|
||||
user := "test"
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "cockroachdb", "v0"), "cockroachdb")
|
||||
|
||||
dbCurrent := coremock.NewMockDatabaseCurrent(gomock.NewController(t))
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
ca, err := pem.DecodeCertificate([]byte(caPem))
|
||||
assert.NoError(t, err)
|
||||
caPriv, err := pem.DecodeKey([]byte(caPrivPem))
|
||||
assert.NoError(t, err)
|
||||
|
||||
dbCurrent.EXPECT().GetCertificate().Times(1).Return(ca)
|
||||
dbCurrent.EXPECT().GetCertificateKey().Times(1).Return(caPriv)
|
||||
k8sClient.EXPECT().ApplySecret(gomock.Any())
|
||||
|
||||
queried := map[string]interface{}{}
|
||||
current := &tree.Tree{
|
||||
Parsed: dbCurrent,
|
||||
}
|
||||
core.SetQueriedForDatabase(queried, current)
|
||||
|
||||
createUser, _, err := AdaptFunc(monitor, namespace, componentLabels)
|
||||
assert.NoError(t, err)
|
||||
query := createUser(user)
|
||||
|
||||
ensure, err := query(k8sClient, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ensure)
|
||||
|
||||
assert.NoError(t, ensure(k8sClient))
|
||||
}
|
||||
|
||||
func TestClient_Adapt2(t *testing.T) {
|
||||
monitor := mntr.Monitor{}
|
||||
namespace := "testNs2"
|
||||
user := "test2"
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "cockroachdb", "v0"), "cockroachdb")
|
||||
|
||||
dbCurrent := coremock.NewMockDatabaseCurrent(gomock.NewController(t))
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
ca, err := pem.DecodeCertificate([]byte(caPem))
|
||||
assert.NoError(t, err)
|
||||
caPriv, err := pem.DecodeKey([]byte(caPrivPem))
|
||||
assert.NoError(t, err)
|
||||
|
||||
dbCurrent.EXPECT().GetCertificate().Times(1).Return(ca)
|
||||
dbCurrent.EXPECT().GetCertificateKey().Times(1).Return(caPriv)
|
||||
k8sClient.EXPECT().ApplySecret(gomock.Any())
|
||||
|
||||
queried := map[string]interface{}{}
|
||||
current := &tree.Tree{
|
||||
Parsed: dbCurrent,
|
||||
}
|
||||
core.SetQueriedForDatabase(queried, current)
|
||||
|
||||
createUser, _, err := AdaptFunc(monitor, namespace, componentLabels)
|
||||
assert.NoError(t, err)
|
||||
query := createUser(user)
|
||||
|
||||
ensure, err := query(k8sClient, queried)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ensure)
|
||||
|
||||
assert.NoError(t, ensure(k8sClient))
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
|
||||
"github.com/caos/orbos/pkg/kubernetes"
|
||||
)
|
||||
|
||||
func QueryCertificates(
|
||||
namespace string,
|
||||
selector *labels.Selector,
|
||||
k8sClient kubernetes.ClientInt,
|
||||
) (
|
||||
[]string,
|
||||
error,
|
||||
) {
|
||||
|
||||
list, err := k8sClient.ListSecrets(namespace, labels.MustK8sMap(selector))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certs := []string{}
|
||||
for _, secret := range list.Items {
|
||||
if strings.HasPrefix(secret.Name, clientSecretPrefix) {
|
||||
certs = append(certs, strings.TrimPrefix(secret.Name, clientSecretPrefix))
|
||||
}
|
||||
}
|
||||
|
||||
return certs, nil
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
kubernetesmock "github.com/caos/orbos/pkg/kubernetes/mock"
|
||||
"github.com/caos/orbos/pkg/labels"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClient_Query0(t *testing.T) {
|
||||
namespace := "testNs"
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "testKind", "v0"), "testComponent")
|
||||
clientLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "testComponent",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"orbos.ch/selectable": "yes",
|
||||
}
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
secretList := &corev1.SecretList{
|
||||
Items: []corev1.Secret{},
|
||||
}
|
||||
|
||||
k8sClient.EXPECT().ListSecrets(namespace, clientLabels).Times(1).Return(secretList, nil)
|
||||
|
||||
users, err := QueryCertificates(namespace, labels.DeriveComponentSelector(componentLabels, false), k8sClient)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, users, []string{})
|
||||
}
|
||||
|
||||
func TestClient_Query(t *testing.T) {
|
||||
namespace := "testNs"
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "testKind", "v0"), "testComponent")
|
||||
clientLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "testComponent",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"orbos.ch/selectable": "yes",
|
||||
}
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
secretList := &corev1.SecretList{
|
||||
Items: []corev1.Secret{{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clientSecretPrefix + "test",
|
||||
},
|
||||
Data: map[string][]byte{},
|
||||
Type: "Opaque",
|
||||
}},
|
||||
}
|
||||
|
||||
k8sClient.EXPECT().ListSecrets(namespace, clientLabels).Times(1).Return(secretList, nil)
|
||||
|
||||
users, err := QueryCertificates(namespace, labels.DeriveComponentSelector(componentLabels, false), k8sClient)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, users, "test")
|
||||
}
|
||||
|
||||
func TestClient_Query2(t *testing.T) {
|
||||
namespace := "testNs"
|
||||
componentLabels := labels.MustForComponent(labels.MustForAPI(labels.MustForOperator("testProd", "testOp", "testVersion"), "testKind", "v0"), "testComponent")
|
||||
clientLabels := map[string]string{
|
||||
"app.kubernetes.io/component": "testComponent",
|
||||
"app.kubernetes.io/managed-by": "testOp",
|
||||
"app.kubernetes.io/part-of": "testProd",
|
||||
"orbos.ch/selectable": "yes",
|
||||
}
|
||||
k8sClient := kubernetesmock.NewMockClientInt(gomock.NewController(t))
|
||||
|
||||
secretList := &corev1.SecretList{
|
||||
Items: []corev1.Secret{{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clientSecretPrefix + "test1",
|
||||
},
|
||||
Data: map[string][]byte{},
|
||||
Type: "Opaque",
|
||||
}, {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clientSecretPrefix + "test2",
|
||||
},
|
||||
Data: map[string][]byte{},
|
||||
Type: "Opaque",
|
||||
}},
|
||||
}
|
||||
|
||||
k8sClient.EXPECT().ListSecrets(namespace, clientLabels).Times(1).Return(secretList, nil)
|
||||
|
||||
users, err := QueryCertificates(namespace, labels.DeriveComponentSelector(componentLabels, false), k8sClient)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, users, []string{"test1", "test2"})
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
)
|
||||
|
||||
type Current struct {
|
||||
CertificateKey *rsa.PrivateKey
|
||||
Certificate []byte
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user