diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..e866d86 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,49 @@ +name: 🐛 Bug report +description: Create a report to help us improve 🎉 +labels: + - bug + +body: + - type: textarea + id: description + attributes: + label: Description + description: A clear and concise description of what the bug is. + validations: + required: true + - type: textarea + id: context + attributes: + label: Additional Context + description: Add any other context about the problem here. + validations: + required: false + - type: textarea + id: logs + attributes: + label: Logs + description: If applicable, add logs to help explain the bug. + render: shell + validations: + required: false + - type: textarea + id: expected_behavior + attributes: + label: Expected Behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + - type: textarea + id: reproduction_steps + attributes: + label: Steps To Reproduce + description: Describe steps to reproduce the behavior + validations: + required: false + - type: textarea + id: version + attributes: + label: Versions + placeholder: v1.2.3 [, Kubernetes 1.21] + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..c07048c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +blank_issues_enabled: false + +# TODO: Redirect support questions +# contact_links: +# - name: ❓ Question +# url: https://github.com///discussions +# about: Ask or discuss with us, we're happy to help 🙋 diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..401c7aa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,67 @@ +name: 🚀 Feature request +description: Suggest an idea for this project 💡 +labels: + - enhancement + +body: + - type: textarea + id: summary + attributes: + label: Summary + value: | + **As** role name\ + **I want** a feature or functionality\ + **So that** I get certain business value + description: This user story helps us to quickly understand what this idea is about. + validations: + required: true + - type: textarea + id: context + attributes: + label: Context + description: Add more information here. You are completely free regarding form and length. + validations: + required: true + - type: textarea + id: out_of_scope + attributes: + label: Out of Scope + description: List aspects that are explicitly not part of this feature + placeholder: | + - ... + - ... + - ... + validations: + required: false + - type: textarea + id: links + attributes: + label: Further links + description: URLs of relevant Git repositories, PRs, Issues, etc. + placeholder: | + - #567 + - https://kubernetes.io/docs/reference/ + validations: + required: false + - type: textarea + id: acceptance_criteria + attributes: + label: Acceptance Criteria + description: If you already have ideas what the detailed requirements are, please list them below in given-when-then expressions. + placeholder: | + - Given a precondition, when an action happens, then expect a result + + ```gherkin + Given a precondition + When an action happens + Then expect a result + ``` + validations: + required: false + - type: textarea + id: implementation_idea + attributes: + label: Implementation Ideas + description: If applicable, shortly list possible implementation ideas + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..c6d8d79 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +## Summary + +* Short summary of what's included in the PR +* Give special note to breaking changes + +## Checklist + +- [ ] Categorize the PR by setting a good title and adding one of the labels: + `bug`, `enhancement`, `documentation`, `change`, `breaking`, `dependency` + as they show up in the changelog +- [ ] Update tests. +- [ ] Link this PR to related issues. + + diff --git a/.github/changelog-configuration.json b/.github/changelog-configuration.json new file mode 100644 index 0000000..8c93e7b --- /dev/null +++ b/.github/changelog-configuration.json @@ -0,0 +1,42 @@ +{ + "pr_template": "- ${{TITLE}} (#${{NUMBER}})", + "categories": [ + { + "title": "## 🚀 Features", + "labels": [ + "enhancement" + ] + }, + { + "title": "## 🛠️ Minor Changes", + "labels": [ + "change" + ] + }, + { + "title": "## 🔎 Breaking Changes", + "labels": [ + "breaking" + ] + }, + { + "title": "## 🐛 Fixes", + "labels": [ + "bug" + ] + }, + { + "title": "## 📄 Documentation", + "labels": [ + "documentation" + ] + }, + { + "title": "## 🔗 Dependency Updates", + "labels": [ + "dependency" + ] + } + ], + "template": "${{CATEGORIZED_COUNT}} changes since ${{FROM_TAG}}\n\n${{CHANGELOG}}" +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..9b1a128 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,30 @@ +name: Build + +on: + pull_request: {} + push: + branches: + - master + +jobs: + go-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Determine Go version from go.mod + run: echo "GO_VERSION=$(go mod edit -json | jq -r .Go)" >> $GITHUB_ENV + + - uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - uses: actions/cache@v3 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Run build + run: make build diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..9441b63 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,82 @@ +name: Docs + +on: + push: + branches: + - master + tags: + - "*" + +jobs: + antora: + runs-on: ubuntu-latest + if: ${{ contains(github.ref, 'tags') }} + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Configure Git + run: | + git config user.name "Antora via GitHub Actions" + git config user.email "actions@github.com" + + - name: Parse semver string + id: semver + uses: booxmedialtd/ws-action-parse-semver@v1 + with: + input_string: ${{ github.ref }} + version_extractor_regex: '\/v(.*)$' + - name: Set variables + run: | + echo "MINOR_VERSION=${{ steps.semver.outputs.major }}.${{ steps.semver.outputs.minor }}" >> $GITHUB_ENV + echo "BRANCH_NAME=docs/v${{ steps.semver.outputs.major }}.${{ steps.semver.outputs.minor }}" >> $GITHUB_ENV + - name: Set branch name for Prerelease + if: ${{ steps.semver.outputs.prerelease != '' }} + run: echo "BRANCH_NAME=${{ env.BRANCH_NAME }}-rc" >> $GITHUB_ENV + + - name: Checkout remote branch if exists + run: git checkout ${{ env.BRANCH_NAME }} + continue-on-error: true + - name: Rebase if possible + run: git rebase ${GITHUB_REF##*/} ${{ env.BRANCH_NAME }} + continue-on-error: true + - name: Create new branch if not existing + run: git switch --create ${{ env.BRANCH_NAME }} + continue-on-error: true + + - name: Patch Antora file for Release + run: yq eval 'del(.prerelease) | del (.display_version) | .version = "${{ env.MINOR_VERSION }}"' -i docs/antora.yml + if: ${{ steps.semver.outputs.prerelease == '' }} + - name: Patch Antora file for Prerelease + run: yq eval 'del (.display_version) | .version = "${{ env.MINOR_VERSION }}", .prerelease = "-${{ steps.semver.outputs.prerelease }}"' -i docs/antora.yml + if: ${{ steps.semver.outputs.prerelease != '' }} + + - name: Commit + run: git commit --all --message "Update version for Antora" + continue-on-error: true + - name: Push + run: git push --atomic --force --set-upstream origin ${{ env.BRANCH_NAME }} + + - name: Cleanup prerelease branch if existing + if: ${{ steps.semver.outputs.prerelease == '' }} + run: git push origin --delete ${{ env.BRANCH_NAME }}-rc + continue-on-error: true + + gh-pages: + runs-on: ubuntu-latest + # The needs+if combo will cause this job to wait until Antora versioning is done for tags, but still run on master branch + needs: antora + if: always() + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git remote set-url origin "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}" + git config user.name "Antora via GitHub Actions" + git config user.email "actions@github.com" + + - name: Publish documentation + run: make docs-publish diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..c82fb46 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,29 @@ +name: Lint + +on: + pull_request: {} + push: + branches: + - master +jobs: + go-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Determine Go version from go.mod + run: echo "GO_VERSION=$(go mod edit -json | jq -r .Go)" >> $GITHUB_ENV + + - uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - uses: actions/cache@v3 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Run linters + run: make lint diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a87687e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,65 @@ +name: Release + +on: + push: + tags: + - "*" + +jobs: + dist: + runs-on: ubuntu-latest + steps: + + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Determine Go version from go.mod + run: echo "GO_VERSION=$(grep "go 1." go.mod | cut -d " " -f 2)" >> $GITHUB_ENV + + - uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - uses: actions/cache@v3 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Login to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Push docker image + run: make docker-push -e IMG_TAG=${GITHUB_REF##*/} + + - name: Build changelog from PRs with labels + id: build_changelog + uses: mikepenz/release-changelog-builder-action@v3 + with: + configuration: ".github/changelog-configuration.json" + # PreReleases still get a changelog, but the next full release gets a diff since the last full release, + # combining possible changelogs of all previous PreReleases in between. PreReleases show a partial changelog + # since last PreRelease. + ignorePreReleases: "${{ !contains(github.ref, '-rc') }}" + outputFile: .github/release-notes.md + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish releases + uses: goreleaser/goreleaser-action@v4 + with: + args: release --release-notes .github/release-notes.md + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..3f1f422 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,30 @@ +name: Test + +on: + push: + branches: + - master + pull_request: {} + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Determine Go version from go.mod + run: echo "GO_VERSION=$(grep "go 1." go.mod | cut -d " " -f 2)" >> $GITHUB_ENV + + - uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - uses: actions/cache@v3 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Run tests + run: make test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8978e19 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# Goreleaser +dist/ +.github/release-notes.md + +# Binaries for programs and plugins +appcat-apiserver +# But don't ignore the appcat APIS! +!apis/appcat + +# temp file, editor and IDE paraphernalia +.idea +.kind +.work +.cache +.public + +# debug +apiserver.local.config +__debug_bin* + +# Kubebuilder +/apis/generated/ diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..29638e8 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,52 @@ +builds: + - binary: appcat + env: + - CGO_ENABLED=0 # this is needed otherwise the Docker image build is faulty + goarch: + - amd64 + - arm64 + goos: + - linux + goarm: + - "8" + +archives: + - format: binary + name_template: "{{ .Binary }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" + +checksum: + name_template: 'checksums.txt' + +snapshot: + name_template: "{{ incpatch .Version }}-snapshot" + +dockers: + - goarch: amd64 + use: buildx + build_flag_templates: + - "--platform=linux/amd64" + image_templates: + - "ghcr.io/vshn/appcat:v{{ .Version }}-amd64" + + - goarch: arm64 + use: buildx + build_flag_templates: + - "--platform=linux/arm64/v8" + image_templates: + - "ghcr.io/vshn/appcat:v{{ .Version }}-arm64" + +docker_manifests: + # For prereleases, updating `latest` does not make sense. + # Only the image for the exact version should be pushed. + - name_template: "{{ if not .Prerelease }}ghcr.io/vshn/appcat:latest{{ end }}" + image_templates: + - "ghcr.io/vshn/appcat:v{{ .Version }}-amd64" + - "ghcr.io/vshn/appcat:v{{ .Version }}-arm64" + + - name_template: "ghcr.io/vshn/appcat:v{{ .Version }}" + image_templates: + - "ghcr.io/vshn/appcat:v{{ .Version }}-amd64" + - "ghcr.io/vshn/appcat:v{{ .Version }}-arm64" + +release: + prerelease: auto diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ca7bdd1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM docker.io/library/alpine:3.15 as runtime + +ENTRYPOINT ["appcat-apiserver"] + +RUN \ + apk add --update --no-cache \ + bash \ + ca-certificates \ + curl + +RUN \ + mkdir /.cache && chmod -R g=u /.cache + +COPY appcat-apiserver /usr/local/bin/ + +RUN chmod a+x /usr/local/bin/appcat-apiserver + +USER 65532:0 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5e98580 --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2023, VSHN AG + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b7a5065 --- /dev/null +++ b/Makefile @@ -0,0 +1,130 @@ + +# Image URL to use all building/pushing image targets +IMG_TAG ?= latest +GHCR_IMG ?= ghcr.io/vshn/appcat-apiserver:$(IMG_TAG) +DOCKER_CMD ?= docker + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +OS := $(shell uname) +ifeq ($(OS), Darwin) + sed ?= gsed +else + sed ?= sed +endif + +# For alpine image it is required the following env before building the application +DOCKER_IMAGE_GOOS = linux +DOCKER_IMAGE_GOARCH = amd64 + +PROJECT_ROOT_DIR = . +PROJECT_NAME ?= appcat-apiserver +PROJECT_OWNER ?= vshn + +PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) +BIN_FILENAME ?= $(PROJECT_DIR)/appcat-apiserver + +## Stackgres CRDs +STACKGRES_VERSION ?= 1.4.3 +STACKGRES_CRD_URL ?= https://gitlab.com/ongresinc/stackgres/-/raw/${STACKGRES_VERSION}/stackgres-k8s/src/common/src/main/resources/crds + +## BUILD:go +go_bin ?= $(PWD)/.work/bin +$(go_bin): + @mkdir -p $@ + +uname_s := $(shell uname -s) +ifeq ($(uname_s),Linux) + distr_protoc := linux-x86_64 +else + distr_protoc := osx-universal_binary +endif + +protoc_bin = $(go_bin)/protoc +$(protoc_bin): export GOBIN = $(go_bin) +$(protoc_bin): | $(go_bin) + @echo "installing protocol buffers with dependencies" + @git clone -q --depth 1 https://github.com/kubernetes/kubernetes.git .work/kubernetes + @go install github.com/gogo/protobuf/protoc-gen-gogo@latest + @go install golang.org/x/tools/cmd/goimports@latest + @wget -q -O $(go_bin)/protoc.zip https://github.com/protocolbuffers/protobuf/releases/download/v22.1/protoc-22.1-$(distr_protoc).zip + @unzip $(go_bin)/protoc.zip -d .work + @rm $(go_bin)/protoc.zip + +-include docs/antora-preview.mk docs/antora-build.mk + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +.PHONY: generate +generate: export PATH := $(go_bin):$(PATH) +generate: $(protoc_bin) ## Generate code with controller-gen and protobuf. + go version + rm -rf apis/generated + go run sigs.k8s.io/controller-tools/cmd/controller-gen paths=./apis/... object crd:crdVersions=v1,allowDangerousTypes=true output:artifacts:config=./apis/generated + go run sigs.k8s.io/controller-tools/cmd/controller-gen rbac:roleName=appcat paths="{./apis/...,./pkg/apiserver/...}" output:artifacts:config=config/apiserver + go generate ./... + go run k8s.io/code-generator/cmd/go-to-protobuf \ + --packages=github.com/vshn/appcat-apiserver/apis/appcat/v1 \ + --output-base=./.work/tmp \ + --go-header-file=./pkg/apiserver/hack/boilerplate.txt \ + --apimachinery-packages='-k8s.io/apimachinery/pkg/util/intstr,-k8s.io/apimachinery/pkg/api/resource,-k8s.io/apimachinery/pkg/runtime/schema,-k8s.io/apimachinery/pkg/runtime,-k8s.io/apimachinery/pkg/apis/meta/v1,-k8s.io/apimachinery/pkg/apis/meta/v1beta1,-k8s.io/api/core/v1,-k8s.io/api/rbac/v1' \ + --proto-import=./.work/kubernetes/vendor/ && \ + mv ./.work/tmp/github.com/vshn/appcat-apiserver/apis/appcat/v1/generated.pb.go ./apis/appcat/v1/ && \ + rm -rf ./.work/tmp + +.PHONY: fmt +fmt: ## Run go fmt against code. + go fmt ./... + +.PHONY: vet +vet: ## Run go vet against code. + go vet ./... + +.PHONY: lint +lint: fmt vet ## All-in-one linting + @echo 'Check for uncommitted changes ...' + git diff --exit-code + +##@ Build + +.PHONY: build +build: export CGO_ENABLED = 0 +build: generate fmt vet ## Build manager binary. +build: + @echo "GOOS=$$(go env GOOS) GOARCH=$$(go env GOARCH)" + go build -o $(BIN_FILENAME) + +.PHONY: test +test: ## Run tests + go test ./... + +.PHONY: docker-build +docker-build: + env CGO_ENABLED=0 GOOS=$(DOCKER_IMAGE_GOOS) GOARCH=$(DOCKER_IMAGE_GOARCH) \ + go build -o ${BIN_FILENAME} + docker build --platform $(DOCKER_IMAGE_GOOS)/$(DOCKER_IMAGE_GOARCH) -t ${GHCR_IMG} . + +.PHONY: docker-build-branchtag +docker-build-branchtag: docker-build ## Build docker image with current branch name + tag=$$(git rev-parse --abbrev-ref HEAD) && \ + docker tag ${GHCR_IMG} ghcr.io/vshn/appcat-apiserver:"$${tag////_}" + +.PHONY: kind-load-branch-tag +kind-load-branch-tag: ## load docker image with current branch tag into kind + tag=$$(git rev-parse --abbrev-ref HEAD) && \ + kind load docker-image --name kindev ghcr.io/vshn/appcat:"$${tag////_}" + +.PHONY: docker-push +docker-push: docker-build ## Push docker image with the manager. + docker push ${GHCR_IMG} + +.PHONY: clean +clean: + rm -rf bin/ appcat .work/ docs/node_modules $docs_out_dir .public .cache apiserver.local.config apis/generated default.sock diff --git a/apis/appcat/v1/appcat_types.go b/apis/appcat/v1/appcat_types.go new file mode 100644 index 0000000..d1a6397 --- /dev/null +++ b/apis/appcat/v1/appcat_types.go @@ -0,0 +1,182 @@ +package v1 + +import ( + "encoding/json" + "regexp" + "strings" + + v1 "github.com/crossplane/crossplane/apis/apiextensions/v1" + "golang.org/x/text/cases" + "golang.org/x/text/language" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/apiserver-runtime/pkg/builder/resource" +) + +// +kubebuilder:rbac:groups="apiextensions.crossplane.io",resources=compositions,verbs=get;list;watch +// +kubebuilder:rbac:groups="stackgres.io",resources=sgbackups,verbs=get;list;watch +// +kubebuilder:rbac:groups="k8up.io",resources=snapshots,verbs=get;list;watch +// +kubebuilder:rbac:groups="vshn.appcat.vshn.io",resources=vshnredis;xvshnpostgresqls,verbs=get;list;watch + +var ( + // OfferedValue is the label value to identify AppCat services + OfferedValue = "true" + + // PrefixAppCatKey is the label and annotation prefix for AppCat services in compositions. + PrefixAppCatKey = "metadata.appcat.vshn.io" + + // OfferedKey is the label key to identify AppCat services + OfferedKey = PrefixAppCatKey + "/offered" +) + +// Resource is the name of this resource in plural form +var Resource = "appcats" + +// +kubebuilder:object:root=true + +// AppCat defines the main object for this API Server +type AppCat struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Details `json:"details,omitempty"` + Plans map[string]VSHNPlan `json:"plans,omitempty"` + Status AppCatStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// AppCatList defines a list of AppCat +type AppCatList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []AppCat `json:"items"` +} + +// Details are fields that are dynamically parsed from the annotations on a composition. +type Details map[string]string + +// VSHNPlan represents a plan for a VSHN service. +// It ignores the scheduling labels and other internal fields. +type VSHNPlan struct { + Note string `json:"note,omitempty"` + // JSize is called JSize because protobuf creates a method Size() + JSize VSHNSize `json:"size,omitempty"` +} + +// VSHNSize describes the aspects of the actual plan. +// This needs to be a separate struct as the protobuf generator can't handle +// embedded struct apparently. +type VSHNSize struct { + CPU string `json:"cpu,omitempty"` + Disk string `json:"disk,omitempty"` + Memory string `json:"memory,omitempty"` +} + +// AppCat needs to implement the builder resource interface +var _ resource.Object = &AppCat{} + +func (in *AppCat) GetObjectMeta() *metav1.ObjectMeta { + return &in.ObjectMeta +} + +func (in *AppCat) NamespaceScoped() bool { + return false +} + +func (in *AppCat) New() runtime.Object { + return &AppCat{} +} + +func (in *AppCat) NewList() runtime.Object { + return &AppCatList{} +} + +// GetGroupVersionResource returns the GroupVersionResource for this resource. +// The resource should be the all lowercase and pluralized kind +func (in *AppCat) GetGroupVersionResource() schema.GroupVersionResource { + return schema.GroupVersionResource{ + Group: GroupVersion.Group, + Version: GroupVersion.Version, + Resource: Resource, + } +} + +// IsStorageVersion returns true if the object is also the internal version -- i.e. is the type defined for the API group or an alias to this object. +// If false, the resource is expected to implement MultiVersionObject interface. +func (in *AppCat) IsStorageVersion() bool { + return true +} + +var _ resource.ObjectList = &AppCatList{} + +func (in *AppCatList) GetListMeta() *metav1.ListMeta { + return &in.ListMeta +} + +// AppCatStatus defines the observed state of AppCat +type AppCatStatus struct { + // CompositionName is the name of the composition + CompositionName string `json:"compositionName,omitempty"` +} + +// NewAppCatFromComposition returns an AppCat based on the given composition +// If the composition does not satisfy one of its rules, the func will return nil +func NewAppCatFromComposition(comp *v1.Composition) *AppCat { + if comp == nil || comp.Labels == nil || comp.Labels[OfferedKey] != OfferedValue { + return nil + } + + appCat := &AppCat{ + ObjectMeta: *comp.ObjectMeta.DeepCopy(), + Status: AppCatStatus{ + CompositionName: comp.Name, + }, + Details: Details{}, + } + appCat.Annotations = nil + appCat.Labels = nil + if comp.Annotations != nil { + for k, v := range comp.Annotations { + if k == PrefixAppCatKey+"/plans" { + parsePlansJSON(v, appCat) + continue + } + if strings.HasPrefix(k, PrefixAppCatKey) { + index := strings.LastIndex(k, "/") + appCat.Details[makeCamelCase(k[index+1:])] = v + } + } + } + + return appCat +} + +// makeCamelCase transforms any string in camel case string +func makeCamelCase(s string) string { + reg, _ := regexp.Compile("[^a-zA-Z0-9-]+") + s = reg.ReplaceAllString(s, "") + s = strings.ToLower(s) + slices := strings.FieldsFunc(s, func(c rune) bool { return c == '-' }) + strCamel := slices[0] + for _, v := range slices[1:] { + strCamel += cases.Title(language.English).String(v) + } + return strCamel +} + +func parsePlansJSON(jsonPlans string, spec *AppCat) { + plans := map[string]VSHNPlan{} + + err := json.Unmarshal([]byte(jsonPlans), &plans) + if err != nil { + spec.Plans = map[string]VSHNPlan{ + "Plans are currently not available": {}, + } + return + } + spec.Plans = plans +} diff --git a/apis/appcat/v1/appcat_types_test.go b/apis/appcat/v1/appcat_types_test.go new file mode 100644 index 0000000..70beec6 --- /dev/null +++ b/apis/appcat/v1/appcat_types_test.go @@ -0,0 +1,167 @@ +package v1 + +import ( + "testing" + + crossplanev1 "github.com/crossplane/crossplane/apis/apiextensions/v1" + "gotest.tools/v3/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestNewAppCatFromComposition(t *testing.T) { + tests := map[string]struct { + composition *crossplanev1.Composition + appCat *AppCat + }{ + "GivenNil_ThenNil": {}, + "GivenNoLabels_ThenNil": { + composition: &crossplanev1.Composition{ + ObjectMeta: metav1.ObjectMeta{ + Labels: nil, + }, + }, + }, + "GivenNonOfferedLabel_ThenNil": { + composition: &crossplanev1.Composition{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + OfferedKey: "false", + }, + }, + }, + }, + "GivenMissingOfferedLabel_ThenNil": { + composition: &crossplanev1.Composition{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "labelname": "labelvalue", + }, + }, + }, + }, + "GivenOfferedLabelWithAppCatAnnotations_ThenReturnAppCat": { + composition: &crossplanev1.Composition{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + OfferedKey: OfferedValue, + }, + Annotations: map[string]string{ + PrefixAppCatKey + "/zone": "rma1", + "non-appcat-prefix" + "/displayname": "comp-1", + "non-appcat-prefix" + "/pippo": "value-23", + }, + Name: "comp-1", + }, + }, + appCat: &AppCat{ + ObjectMeta: metav1.ObjectMeta{ + Name: "comp-1", + }, + + Details: map[string]string{ + "zone": "rma1", + }, + + Status: AppCatStatus{ + CompositionName: "comp-1", + }, + }, + }, + "GivenWithPlans_ThenReturnAppCatWithPlans": { + composition: &crossplanev1.Composition{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + OfferedKey: OfferedValue, + }, + Annotations: map[string]string{ + PrefixAppCatKey + "/plans": `{"standard-4": { "note": "test", "size": { "cpu": "900m", "disk": "40Gi", "enabled": true, "memory": "3776Mi" } } }`, + }, + Name: "comp-1", + }, + }, + appCat: &AppCat{ + ObjectMeta: metav1.ObjectMeta{ + Name: "comp-1", + }, + Details: Details{}, + Plans: map[string]VSHNPlan{ + "standard-4": { + Note: "test", + JSize: VSHNSize{ + CPU: "900m", + Disk: "40Gi", + Memory: "3776Mi", + }, + }, + }, + + Status: AppCatStatus{ + CompositionName: "comp-1", + }, + }, + }, + "GivenInvalidPlans_ThenReturnAppCatWithMessage": { + composition: &crossplanev1.Composition{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + OfferedKey: OfferedValue, + }, + Annotations: map[string]string{ + PrefixAppCatKey + "/plans": "imnotajson", + }, + Name: "comp-1", + }, + }, + appCat: &AppCat{ + ObjectMeta: metav1.ObjectMeta{ + Name: "comp-1", + }, + + Details: Details{}, + + Plans: map[string]VSHNPlan{ + "Plans are currently not available": {}, + }, + + Status: AppCatStatus{ + CompositionName: "comp-1", + }, + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + actualAppCat := NewAppCatFromComposition(tt.composition) + assert.DeepEqual(t, tt.appCat, actualAppCat) + }) + } +} + +func TestMakeCamelCase(t *testing.T) { + tests := map[string]struct { + input, output string + }{ + "GivenWrongK8sStrCase1_ThenCamelCaseStr": { + input: "-k8s-name-type-", + output: "k8sNameType", + }, + "GivenWrongK8sStrCase2_ThenCamelCaseStr": { + input: "::-k8s-name-.type/", + output: "k8sNameType", + }, + "GivenWrongK8sStrCase3_ThenCamelCaseStr": { + input: "-k8S-nA%me-tyPe%", + output: "k8sNameType", + }, + "GivenCorrectK8sStr_ThenCamelCaseStr": { + input: "k8s-name-type", + output: "k8sNameType", + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + actualStr := makeCamelCase(tt.input) + assert.Equal(t, tt.output, actualStr) + }) + } +} diff --git a/apis/appcat/v1/generated.pb.go b/apis/appcat/v1/generated.pb.go new file mode 100644 index 0000000..e626cb7 --- /dev/null +++ b/apis/appcat/v1/generated.pb.go @@ -0,0 +1,3251 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: .work/tmp/github.com/vshn/appcat-apiserver/apis/appcat/v1/generated.proto + +package v1 + +import ( + fmt "fmt" + + io "io" + + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + runtime "k8s.io/apimachinery/pkg/runtime" + + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +func (m *AppCat) Reset() { *m = AppCat{} } +func (*AppCat) ProtoMessage() {} +func (*AppCat) Descriptor() ([]byte, []int) { + return fileDescriptor_ade2e39f600d64ee, []int{0} +} +func (m *AppCat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AppCat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *AppCat) XXX_Merge(src proto.Message) { + xxx_messageInfo_AppCat.Merge(m, src) +} +func (m *AppCat) XXX_Size() int { + return m.Size() +} +func (m *AppCat) XXX_DiscardUnknown() { + xxx_messageInfo_AppCat.DiscardUnknown(m) +} + +var xxx_messageInfo_AppCat proto.InternalMessageInfo + +func (m *AppCatList) Reset() { *m = AppCatList{} } +func (*AppCatList) ProtoMessage() {} +func (*AppCatList) Descriptor() ([]byte, []int) { + return fileDescriptor_ade2e39f600d64ee, []int{1} +} +func (m *AppCatList) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AppCatList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *AppCatList) XXX_Merge(src proto.Message) { + xxx_messageInfo_AppCatList.Merge(m, src) +} +func (m *AppCatList) XXX_Size() int { + return m.Size() +} +func (m *AppCatList) XXX_DiscardUnknown() { + xxx_messageInfo_AppCatList.DiscardUnknown(m) +} + +var xxx_messageInfo_AppCatList proto.InternalMessageInfo + +func (m *AppCatStatus) Reset() { *m = AppCatStatus{} } +func (*AppCatStatus) ProtoMessage() {} +func (*AppCatStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_ade2e39f600d64ee, []int{2} +} +func (m *AppCatStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AppCatStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *AppCatStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_AppCatStatus.Merge(m, src) +} +func (m *AppCatStatus) XXX_Size() int { + return m.Size() +} +func (m *AppCatStatus) XXX_DiscardUnknown() { + xxx_messageInfo_AppCatStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_AppCatStatus proto.InternalMessageInfo + +func (m *SGBackupInfo) Reset() { *m = SGBackupInfo{} } +func (*SGBackupInfo) ProtoMessage() {} +func (*SGBackupInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_ade2e39f600d64ee, []int{3} +} +func (m *SGBackupInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SGBackupInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *SGBackupInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_SGBackupInfo.Merge(m, src) +} +func (m *SGBackupInfo) XXX_Size() int { + return m.Size() +} +func (m *SGBackupInfo) XXX_DiscardUnknown() { + xxx_messageInfo_SGBackupInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_SGBackupInfo proto.InternalMessageInfo + +func (m *VSHNPlan) Reset() { *m = VSHNPlan{} } +func (*VSHNPlan) ProtoMessage() {} +func (*VSHNPlan) Descriptor() ([]byte, []int) { + return fileDescriptor_ade2e39f600d64ee, []int{4} +} +func (m *VSHNPlan) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VSHNPlan) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *VSHNPlan) XXX_Merge(src proto.Message) { + xxx_messageInfo_VSHNPlan.Merge(m, src) +} +func (m *VSHNPlan) XXX_Size() int { + return m.Size() +} +func (m *VSHNPlan) XXX_DiscardUnknown() { + xxx_messageInfo_VSHNPlan.DiscardUnknown(m) +} + +var xxx_messageInfo_VSHNPlan proto.InternalMessageInfo + +func (m *VSHNPostgresBackup) Reset() { *m = VSHNPostgresBackup{} } +func (*VSHNPostgresBackup) ProtoMessage() {} +func (*VSHNPostgresBackup) Descriptor() ([]byte, []int) { + return fileDescriptor_ade2e39f600d64ee, []int{5} +} +func (m *VSHNPostgresBackup) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VSHNPostgresBackup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *VSHNPostgresBackup) XXX_Merge(src proto.Message) { + xxx_messageInfo_VSHNPostgresBackup.Merge(m, src) +} +func (m *VSHNPostgresBackup) XXX_Size() int { + return m.Size() +} +func (m *VSHNPostgresBackup) XXX_DiscardUnknown() { + xxx_messageInfo_VSHNPostgresBackup.DiscardUnknown(m) +} + +var xxx_messageInfo_VSHNPostgresBackup proto.InternalMessageInfo + +func (m *VSHNPostgresBackupList) Reset() { *m = VSHNPostgresBackupList{} } +func (*VSHNPostgresBackupList) ProtoMessage() {} +func (*VSHNPostgresBackupList) Descriptor() ([]byte, []int) { + return fileDescriptor_ade2e39f600d64ee, []int{6} +} +func (m *VSHNPostgresBackupList) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VSHNPostgresBackupList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *VSHNPostgresBackupList) XXX_Merge(src proto.Message) { + xxx_messageInfo_VSHNPostgresBackupList.Merge(m, src) +} +func (m *VSHNPostgresBackupList) XXX_Size() int { + return m.Size() +} +func (m *VSHNPostgresBackupList) XXX_DiscardUnknown() { + xxx_messageInfo_VSHNPostgresBackupList.DiscardUnknown(m) +} + +var xxx_messageInfo_VSHNPostgresBackupList proto.InternalMessageInfo + +func (m *VSHNPostgresBackupStatus) Reset() { *m = VSHNPostgresBackupStatus{} } +func (*VSHNPostgresBackupStatus) ProtoMessage() {} +func (*VSHNPostgresBackupStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_ade2e39f600d64ee, []int{7} +} +func (m *VSHNPostgresBackupStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VSHNPostgresBackupStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *VSHNPostgresBackupStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_VSHNPostgresBackupStatus.Merge(m, src) +} +func (m *VSHNPostgresBackupStatus) XXX_Size() int { + return m.Size() +} +func (m *VSHNPostgresBackupStatus) XXX_DiscardUnknown() { + xxx_messageInfo_VSHNPostgresBackupStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_VSHNPostgresBackupStatus proto.InternalMessageInfo + +func (m *VSHNRedisBackup) Reset() { *m = VSHNRedisBackup{} } +func (*VSHNRedisBackup) ProtoMessage() {} +func (*VSHNRedisBackup) Descriptor() ([]byte, []int) { + return fileDescriptor_ade2e39f600d64ee, []int{8} +} +func (m *VSHNRedisBackup) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VSHNRedisBackup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *VSHNRedisBackup) XXX_Merge(src proto.Message) { + xxx_messageInfo_VSHNRedisBackup.Merge(m, src) +} +func (m *VSHNRedisBackup) XXX_Size() int { + return m.Size() +} +func (m *VSHNRedisBackup) XXX_DiscardUnknown() { + xxx_messageInfo_VSHNRedisBackup.DiscardUnknown(m) +} + +var xxx_messageInfo_VSHNRedisBackup proto.InternalMessageInfo + +func (m *VSHNRedisBackupList) Reset() { *m = VSHNRedisBackupList{} } +func (*VSHNRedisBackupList) ProtoMessage() {} +func (*VSHNRedisBackupList) Descriptor() ([]byte, []int) { + return fileDescriptor_ade2e39f600d64ee, []int{9} +} +func (m *VSHNRedisBackupList) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VSHNRedisBackupList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *VSHNRedisBackupList) XXX_Merge(src proto.Message) { + xxx_messageInfo_VSHNRedisBackupList.Merge(m, src) +} +func (m *VSHNRedisBackupList) XXX_Size() int { + return m.Size() +} +func (m *VSHNRedisBackupList) XXX_DiscardUnknown() { + xxx_messageInfo_VSHNRedisBackupList.DiscardUnknown(m) +} + +var xxx_messageInfo_VSHNRedisBackupList proto.InternalMessageInfo + +func (m *VSHNRedisBackupStatus) Reset() { *m = VSHNRedisBackupStatus{} } +func (*VSHNRedisBackupStatus) ProtoMessage() {} +func (*VSHNRedisBackupStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_ade2e39f600d64ee, []int{10} +} +func (m *VSHNRedisBackupStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VSHNRedisBackupStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *VSHNRedisBackupStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_VSHNRedisBackupStatus.Merge(m, src) +} +func (m *VSHNRedisBackupStatus) XXX_Size() int { + return m.Size() +} +func (m *VSHNRedisBackupStatus) XXX_DiscardUnknown() { + xxx_messageInfo_VSHNRedisBackupStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_VSHNRedisBackupStatus proto.InternalMessageInfo + +func (m *VSHNSize) Reset() { *m = VSHNSize{} } +func (*VSHNSize) ProtoMessage() {} +func (*VSHNSize) Descriptor() ([]byte, []int) { + return fileDescriptor_ade2e39f600d64ee, []int{11} +} +func (m *VSHNSize) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VSHNSize) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *VSHNSize) XXX_Merge(src proto.Message) { + xxx_messageInfo_VSHNSize.Merge(m, src) +} +func (m *VSHNSize) XXX_Size() int { + return m.Size() +} +func (m *VSHNSize) XXX_DiscardUnknown() { + xxx_messageInfo_VSHNSize.DiscardUnknown(m) +} + +var xxx_messageInfo_VSHNSize proto.InternalMessageInfo + +func init() { + proto.RegisterType((*AppCat)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.AppCat") + proto.RegisterMapType((Details)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.AppCat.DetailsEntry") + proto.RegisterMapType((map[string]VSHNPlan)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.AppCat.PlansEntry") + proto.RegisterType((*AppCatList)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.AppCatList") + proto.RegisterType((*AppCatStatus)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.AppCatStatus") + proto.RegisterType((*SGBackupInfo)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.SGBackupInfo") + proto.RegisterType((*VSHNPlan)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.VSHNPlan") + proto.RegisterType((*VSHNPostgresBackup)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.VSHNPostgresBackup") + proto.RegisterType((*VSHNPostgresBackupList)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.VSHNPostgresBackupList") + proto.RegisterType((*VSHNPostgresBackupStatus)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.VSHNPostgresBackupStatus") + proto.RegisterType((*VSHNRedisBackup)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.VSHNRedisBackup") + proto.RegisterType((*VSHNRedisBackupList)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.VSHNRedisBackupList") + proto.RegisterType((*VSHNRedisBackupStatus)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.VSHNRedisBackupStatus") + proto.RegisterType((*VSHNSize)(nil), "github.com.vshn.appcat_apiserver.apis.appcat.v1.VSHNSize") +} + +func init() { + proto.RegisterFile(".work/tmp/github.com/vshn/appcat-apiserver/apis/appcat/v1/generated.proto", fileDescriptor_ade2e39f600d64ee) +} + +var fileDescriptor_ade2e39f600d64ee = []byte{ + // 954 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x4f, 0x6f, 0x1b, 0x45, + 0x14, 0xcf, 0xda, 0xce, 0x9f, 0x3e, 0x07, 0x92, 0x0e, 0x14, 0x8c, 0x25, 0xdc, 0xc8, 0x07, 0x54, + 0x21, 0x32, 0x4b, 0x22, 0x04, 0xa5, 0x12, 0x12, 0xdd, 0xb8, 0x80, 0x51, 0x9b, 0x86, 0x49, 0x8b, + 0x10, 0x8a, 0x04, 0xe3, 0xf5, 0xd4, 0x1e, 0x9c, 0xdd, 0x59, 0x76, 0xc6, 0x2e, 0xc9, 0x89, 0x33, + 0x27, 0x3e, 0x07, 0x17, 0xbe, 0x05, 0xca, 0xb1, 0xc7, 0x8a, 0x43, 0xd5, 0xb8, 0x07, 0xc4, 0x57, + 0xe0, 0x84, 0x66, 0x67, 0xbc, 0x5e, 0xff, 0x6b, 0x13, 0xb7, 0xca, 0x6d, 0xe6, 0xcd, 0xbc, 0xdf, + 0xef, 0xbd, 0x37, 0xbf, 0xf7, 0xec, 0x85, 0x3a, 0x7e, 0x28, 0xe2, 0x8e, 0xab, 0x82, 0xc8, 0x6d, + 0x71, 0xd5, 0xee, 0x36, 0xb0, 0x2f, 0x02, 0xb7, 0x27, 0xdb, 0xa1, 0x4b, 0xa3, 0xc8, 0xa7, 0x6a, + 0x93, 0x46, 0x5c, 0xb2, 0xb8, 0xc7, 0x62, 0x57, 0xaf, 0xac, 0xd5, 0xed, 0x6d, 0xb9, 0x2d, 0x16, + 0xb2, 0x98, 0x2a, 0xd6, 0xc4, 0x51, 0x2c, 0x94, 0x40, 0x19, 0x00, 0xac, 0x01, 0xb0, 0xb9, 0xfa, + 0x43, 0x0a, 0x80, 0xf5, 0xca, 0x5a, 0x71, 0x6f, 0xab, 0xbc, 0x99, 0x61, 0x6c, 0x89, 0x96, 0x70, + 0x13, 0x9c, 0x46, 0xf7, 0x41, 0xb2, 0x4b, 0x36, 0xc9, 0xca, 0xe0, 0x97, 0x3f, 0xea, 0x5c, 0x97, + 0x98, 0x0b, 0x1d, 0x46, 0x40, 0xfd, 0x36, 0x0f, 0x59, 0x7c, 0xe4, 0x46, 0x9d, 0x96, 0x89, 0x2b, + 0x60, 0x8a, 0x4e, 0x89, 0xaa, 0xec, 0xce, 0xf2, 0x8a, 0xbb, 0xa1, 0xe2, 0x01, 0x9b, 0x70, 0xf8, + 0xf8, 0x45, 0x0e, 0xd2, 0x6f, 0xb3, 0x80, 0x8e, 0xfb, 0x55, 0xff, 0x29, 0xc0, 0xd2, 0xcd, 0x28, + 0xda, 0xa1, 0x0a, 0xfd, 0x08, 0x2b, 0x3a, 0x9c, 0x26, 0x55, 0xb4, 0xe4, 0x6c, 0x38, 0xd7, 0x8a, + 0xdb, 0x1f, 0x62, 0x83, 0x8a, 0xb3, 0xa8, 0x38, 0xea, 0xb4, 0x4c, 0x4d, 0xf4, 0x6d, 0xdc, 0xdb, + 0xc2, 0x77, 0x1b, 0x3f, 0x31, 0x5f, 0xdd, 0x61, 0x8a, 0x7a, 0xe8, 0xe4, 0xc9, 0xd5, 0x85, 0xfe, + 0x93, 0xab, 0x30, 0xb4, 0x91, 0x14, 0x15, 0x1d, 0xc3, 0x72, 0x93, 0x29, 0xca, 0x0f, 0x65, 0x29, + 0xb7, 0x91, 0xbf, 0x56, 0xdc, 0xae, 0xe1, 0x73, 0x56, 0x1f, 0x9b, 0x58, 0x71, 0xcd, 0xc0, 0xdc, + 0x0a, 0x55, 0x7c, 0xe4, 0x95, 0x2d, 0xe9, 0xb2, 0xb5, 0xfe, 0x37, 0x5c, 0x92, 0x01, 0x21, 0x6a, + 0xc1, 0x62, 0x74, 0x48, 0x43, 0x59, 0xca, 0x27, 0xcc, 0xde, 0xbc, 0xcc, 0x7b, 0x1a, 0xc4, 0xf0, + 0xbe, 0x66, 0x79, 0x17, 0x13, 0x1b, 0x31, 0xf8, 0x88, 0xc1, 0x92, 0x54, 0x54, 0x75, 0x65, 0xa9, + 0x90, 0x14, 0xf1, 0xb3, 0x39, 0x99, 0xf6, 0x13, 0x10, 0xef, 0x75, 0x4b, 0xb2, 0x64, 0xf6, 0xc4, + 0x82, 0x97, 0x6f, 0xc0, 0x6a, 0xb6, 0x08, 0x68, 0x1d, 0xf2, 0x1d, 0x76, 0x94, 0x3c, 0xdc, 0x25, + 0xa2, 0x97, 0xe8, 0x4d, 0x58, 0xec, 0xd1, 0xc3, 0x2e, 0x2b, 0xe5, 0x12, 0x9b, 0xd9, 0xdc, 0xc8, + 0x5d, 0x77, 0xca, 0x12, 0x60, 0x98, 0xc6, 0x14, 0xcf, 0xbb, 0x59, 0xcf, 0xe2, 0xf6, 0xa7, 0xe7, + 0xce, 0xe0, 0xdb, 0xfd, 0xaf, 0x76, 0x35, 0x43, 0x86, 0xb4, 0x7a, 0xe2, 0x00, 0x98, 0xcc, 0x6e, + 0x73, 0xa9, 0xd0, 0xc1, 0x84, 0xda, 0xf0, 0xd9, 0xd4, 0xa6, 0xbd, 0x13, 0xad, 0xad, 0xdb, 0xca, + 0xac, 0x0c, 0x2c, 0x19, 0xa5, 0x1d, 0xc0, 0x22, 0x57, 0x2c, 0x18, 0xe8, 0xec, 0x93, 0x39, 0xdf, + 0x60, 0xf8, 0xc4, 0x75, 0x8d, 0x46, 0x0c, 0x68, 0xf5, 0x1b, 0x58, 0xcd, 0xbe, 0x11, 0xba, 0x09, + 0x6b, 0xbe, 0x08, 0x22, 0x21, 0xb9, 0xe2, 0x22, 0xdc, 0xa5, 0x01, 0x33, 0xd5, 0xf4, 0xde, 0xb6, + 0xee, 0x6b, 0x3b, 0xa3, 0xc7, 0x64, 0xfc, 0x7e, 0xf5, 0xaf, 0x1c, 0xac, 0xee, 0x7f, 0xe9, 0x51, + 0xbf, 0xd3, 0x8d, 0xea, 0xe1, 0x03, 0x81, 0x9a, 0x00, 0x22, 0xed, 0xa1, 0x57, 0xda, 0x8f, 0x19, + 0x5c, 0xf4, 0x1d, 0x2c, 0x47, 0xb1, 0xf0, 0x99, 0x94, 0xf6, 0xad, 0x37, 0x67, 0x52, 0xd8, 0x41, + 0x82, 0x09, 0x7d, 0x78, 0xeb, 0x17, 0xc5, 0x42, 0xc9, 0x45, 0xe8, 0xad, 0x0d, 0x5a, 0x6f, 0xcf, + 0xa0, 0x90, 0x01, 0x1c, 0xea, 0xc1, 0xe5, 0x46, 0x9a, 0x4d, 0x1c, 0x50, 0x9d, 0x69, 0x29, 0x3f, + 0x0f, 0xc7, 0x3b, 0x96, 0xe3, 0xb2, 0x37, 0x8e, 0x47, 0x26, 0x29, 0xaa, 0xbf, 0x39, 0xb0, 0x32, + 0x90, 0x1f, 0xda, 0x80, 0x42, 0x28, 0xd4, 0xe0, 0x35, 0x56, 0x2d, 0x50, 0x61, 0x57, 0x28, 0x46, + 0x92, 0x13, 0x74, 0x00, 0x05, 0xc9, 0x8f, 0x5f, 0x4e, 0xe9, 0xfb, 0xfc, 0x98, 0x0d, 0x95, 0xf2, + 0xb5, 0xde, 0x92, 0x04, 0xb5, 0xfa, 0xaf, 0x03, 0x28, 0x09, 0x46, 0x48, 0xd5, 0x8a, 0x99, 0x34, + 0x19, 0x5c, 0xc0, 0xa4, 0xfd, 0x39, 0x1d, 0x42, 0x26, 0xb1, 0xfa, 0x7c, 0x2d, 0x3c, 0x12, 0xf6, + 0xf3, 0x07, 0x52, 0xf5, 0xa9, 0x03, 0x6f, 0x4d, 0x3a, 0x5d, 0x40, 0xaf, 0xb7, 0x47, 0x7b, 0x7d, + 0xe7, 0x15, 0xa4, 0x3a, 0xa3, 0xef, 0xff, 0xc8, 0x41, 0x69, 0x56, 0x5d, 0xd0, 0xbd, 0x61, 0x2b, + 0x39, 0xf3, 0xc8, 0xbc, 0x38, 0xb5, 0x8d, 0xe2, 0x69, 0x6d, 0x34, 0x57, 0xab, 0x5e, 0x39, 0x6b, + 0x0b, 0xa1, 0x1a, 0xac, 0xeb, 0xc2, 0x36, 0xa8, 0x64, 0xf5, 0x50, 0x2a, 0x1a, 0xfa, 0x2c, 0xe9, + 0xdc, 0x4b, 0x5e, 0xc9, 0x96, 0x65, 0xbd, 0x36, 0x76, 0x4e, 0x26, 0x3c, 0xaa, 0xcf, 0x1c, 0x58, + 0xd3, 0xc5, 0x22, 0xac, 0xc9, 0x2f, 0x4e, 0xf8, 0xe1, 0x98, 0xf0, 0xbf, 0x98, 0x4b, 0x0d, 0x99, + 0x98, 0x5f, 0xa0, 0xfa, 0xbf, 0x1d, 0x78, 0x63, 0xcc, 0xe3, 0x02, 0x24, 0xcf, 0x46, 0x25, 0xff, + 0xf9, 0xcb, 0x26, 0x39, 0x43, 0xef, 0x7f, 0x3a, 0x70, 0x65, 0x6a, 0x39, 0x50, 0x19, 0x72, 0xbc, + 0x69, 0xc7, 0x2a, 0x58, 0xdf, 0x5c, 0xbd, 0x46, 0x72, 0xbc, 0x89, 0x6e, 0x43, 0xa1, 0x49, 0xd5, + 0x60, 0xa4, 0xbe, 0x7f, 0xb6, 0xb4, 0xef, 0xf1, 0x80, 0x0d, 0x07, 0x74, 0x8d, 0xea, 0x01, 0xad, + 0x51, 0xd0, 0x07, 0xb0, 0xc2, 0x47, 0x45, 0x98, 0x16, 0x26, 0x15, 0x5f, 0x7a, 0xa3, 0x2a, 0xcd, + 0xf0, 0xd7, 0x23, 0x18, 0xbd, 0x0b, 0x79, 0x3f, 0xea, 0xda, 0x20, 0x8b, 0xd6, 0x29, 0xbf, 0xb3, + 0x77, 0x9f, 0x68, 0xbb, 0xfe, 0x6d, 0x68, 0x72, 0xd9, 0x31, 0xff, 0x8e, 0x32, 0xd4, 0x5c, 0x76, + 0x48, 0x72, 0x82, 0xde, 0x83, 0xa5, 0x80, 0x05, 0x22, 0x3e, 0xb2, 0xc4, 0xa9, 0x06, 0xee, 0x24, + 0x56, 0x62, 0x4f, 0xbd, 0xfb, 0x27, 0xa7, 0x95, 0x85, 0x47, 0xa7, 0x95, 0x85, 0xc7, 0xa7, 0x95, + 0x85, 0x5f, 0xfb, 0x15, 0xe7, 0xa4, 0x5f, 0x71, 0x1e, 0xf5, 0x2b, 0xce, 0xe3, 0x7e, 0xc5, 0x79, + 0xda, 0xaf, 0x38, 0xbf, 0x3f, 0xab, 0x2c, 0x7c, 0x7f, 0xde, 0x2f, 0x95, 0xff, 0x03, 0x00, 0x00, + 0xff, 0xff, 0xb6, 0x7f, 0x37, 0x83, 0xe5, 0x0c, 0x00, 0x00, +} + +func (m *AppCat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AppCat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AppCat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.Plans) > 0 { + keysForPlans := make([]string, 0, len(m.Plans)) + for k := range m.Plans { + keysForPlans = append(keysForPlans, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForPlans) + for iNdEx := len(keysForPlans) - 1; iNdEx >= 0; iNdEx-- { + v := m.Plans[string(keysForPlans[iNdEx])] + baseI := i + { + size, err := (&v).MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + i -= len(keysForPlans[iNdEx]) + copy(dAtA[i:], keysForPlans[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(keysForPlans[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Details) > 0 { + keysForDetails := make([]string, 0, len(m.Details)) + for k := range m.Details { + keysForDetails = append(keysForDetails, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForDetails) + for iNdEx := len(keysForDetails) - 1; iNdEx >= 0; iNdEx-- { + v := m.Details[string(keysForDetails[iNdEx])] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintGenerated(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(keysForDetails[iNdEx]) + copy(dAtA[i:], keysForDetails[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(keysForDetails[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.ObjectMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *AppCatList) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AppCatList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AppCatList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Items) > 0 { + for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Items[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.ListMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *AppCatStatus) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AppCatStatus) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AppCatStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.CompositionName) + copy(dAtA[i:], m.CompositionName) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.CompositionName))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *SGBackupInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SGBackupInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SGBackupInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.BackupInformation.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.Process.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.ObjectMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *VSHNPlan) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VSHNPlan) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VSHNPlan) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.JSize.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + i -= len(m.Note) + copy(dAtA[i:], m.Note) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Note))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *VSHNPostgresBackup) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VSHNPostgresBackup) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VSHNPostgresBackup) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.ObjectMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *VSHNPostgresBackupList) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VSHNPostgresBackupList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VSHNPostgresBackupList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Items) > 0 { + for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Items[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.ListMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *VSHNPostgresBackupStatus) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VSHNPostgresBackupStatus) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VSHNPostgresBackupStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.DatabaseInstance) + copy(dAtA[i:], m.DatabaseInstance) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.DatabaseInstance))) + i-- + dAtA[i] = 0x1a + if m.BackupInformation != nil { + { + size, err := m.BackupInformation.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Process != nil { + { + size, err := m.Process.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *VSHNRedisBackup) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VSHNRedisBackup) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VSHNRedisBackup) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.ObjectMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *VSHNRedisBackupList) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VSHNRedisBackupList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VSHNRedisBackupList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Items) > 0 { + for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Items[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.ListMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *VSHNRedisBackupStatus) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VSHNRedisBackupStatus) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VSHNRedisBackupStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.Instance) + copy(dAtA[i:], m.Instance) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Instance))) + i-- + dAtA[i] = 0x1a + { + size, err := m.Date.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *VSHNSize) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *VSHNSize) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VSHNSize) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.Memory) + copy(dAtA[i:], m.Memory) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Memory))) + i-- + dAtA[i] = 0x1a + i -= len(m.Disk) + copy(dAtA[i:], m.Disk) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Disk))) + i-- + dAtA[i] = 0x12 + i -= len(m.CPU) + copy(dAtA[i:], m.CPU) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.CPU))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int { + offset -= sovGenerated(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *AppCat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ObjectMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Details) > 0 { + for k, v := range m.Details { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v))) + n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) + } + } + if len(m.Plans) > 0 { + for k, v := range m.Plans { + _ = k + _ = v + l = v.Size() + mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + l + sovGenerated(uint64(l)) + n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) + } + } + l = m.Status.Size() + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *AppCatList) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ListMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Items) > 0 { + for _, e := range m.Items { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *AppCatStatus) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.CompositionName) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *SGBackupInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ObjectMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = m.Process.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = m.BackupInformation.Size() + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *VSHNPlan) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Note) + n += 1 + l + sovGenerated(uint64(l)) + l = m.JSize.Size() + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *VSHNPostgresBackup) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ObjectMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *VSHNPostgresBackupList) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ListMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Items) > 0 { + for _, e := range m.Items { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *VSHNPostgresBackupStatus) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Process != nil { + l = m.Process.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if m.BackupInformation != nil { + l = m.BackupInformation.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + l = len(m.DatabaseInstance) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *VSHNRedisBackup) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ObjectMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *VSHNRedisBackupList) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ListMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Items) > 0 { + for _, e := range m.Items { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *VSHNRedisBackupStatus) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ID) + n += 1 + l + sovGenerated(uint64(l)) + l = m.Date.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Instance) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *VSHNSize) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.CPU) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Disk) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Memory) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func sovGenerated(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenerated(x uint64) (n int) { + return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *AppCat) String() string { + if this == nil { + return "nil" + } + keysForDetails := make([]string, 0, len(this.Details)) + for k := range this.Details { + keysForDetails = append(keysForDetails, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForDetails) + mapStringForDetails := "Details{" + for _, k := range keysForDetails { + mapStringForDetails += fmt.Sprintf("%v: %v,", k, this.Details[k]) + } + mapStringForDetails += "}" + keysForPlans := make([]string, 0, len(this.Plans)) + for k := range this.Plans { + keysForPlans = append(keysForPlans, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForPlans) + mapStringForPlans := "map[string]VSHNPlan{" + for _, k := range keysForPlans { + mapStringForPlans += fmt.Sprintf("%v: %v,", k, this.Plans[k]) + } + mapStringForPlans += "}" + s := strings.Join([]string{`&AppCat{`, + `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`, + `Details:` + mapStringForDetails + `,`, + `Plans:` + mapStringForPlans + `,`, + `Status:` + strings.Replace(strings.Replace(this.Status.String(), "AppCatStatus", "AppCatStatus", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *AppCatList) String() string { + if this == nil { + return "nil" + } + repeatedStringForItems := "[]AppCat{" + for _, f := range this.Items { + repeatedStringForItems += strings.Replace(strings.Replace(f.String(), "AppCat", "AppCat", 1), `&`, ``, 1) + "," + } + repeatedStringForItems += "}" + s := strings.Join([]string{`&AppCatList{`, + `ListMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ListMeta), "ListMeta", "v1.ListMeta", 1), `&`, ``, 1) + `,`, + `Items:` + repeatedStringForItems + `,`, + `}`, + }, "") + return s +} +func (this *AppCatStatus) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&AppCatStatus{`, + `CompositionName:` + fmt.Sprintf("%v", this.CompositionName) + `,`, + `}`, + }, "") + return s +} +func (this *SGBackupInfo) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&SGBackupInfo{`, + `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`, + `Process:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Process), "RawExtension", "runtime.RawExtension", 1), `&`, ``, 1) + `,`, + `BackupInformation:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.BackupInformation), "RawExtension", "runtime.RawExtension", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *VSHNPlan) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&VSHNPlan{`, + `Note:` + fmt.Sprintf("%v", this.Note) + `,`, + `JSize:` + strings.Replace(strings.Replace(this.JSize.String(), "VSHNSize", "VSHNSize", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *VSHNPostgresBackup) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&VSHNPostgresBackup{`, + `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`, + `Status:` + strings.Replace(strings.Replace(this.Status.String(), "VSHNPostgresBackupStatus", "VSHNPostgresBackupStatus", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *VSHNPostgresBackupList) String() string { + if this == nil { + return "nil" + } + repeatedStringForItems := "[]VSHNPostgresBackup{" + for _, f := range this.Items { + repeatedStringForItems += strings.Replace(strings.Replace(f.String(), "VSHNPostgresBackup", "VSHNPostgresBackup", 1), `&`, ``, 1) + "," + } + repeatedStringForItems += "}" + s := strings.Join([]string{`&VSHNPostgresBackupList{`, + `ListMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ListMeta), "ListMeta", "v1.ListMeta", 1), `&`, ``, 1) + `,`, + `Items:` + repeatedStringForItems + `,`, + `}`, + }, "") + return s +} +func (this *VSHNPostgresBackupStatus) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&VSHNPostgresBackupStatus{`, + `Process:` + strings.Replace(fmt.Sprintf("%v", this.Process), "RawExtension", "runtime.RawExtension", 1) + `,`, + `BackupInformation:` + strings.Replace(fmt.Sprintf("%v", this.BackupInformation), "RawExtension", "runtime.RawExtension", 1) + `,`, + `DatabaseInstance:` + fmt.Sprintf("%v", this.DatabaseInstance) + `,`, + `}`, + }, "") + return s +} +func (this *VSHNRedisBackup) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&VSHNRedisBackup{`, + `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`, + `Status:` + strings.Replace(strings.Replace(this.Status.String(), "VSHNRedisBackupStatus", "VSHNRedisBackupStatus", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *VSHNRedisBackupList) String() string { + if this == nil { + return "nil" + } + repeatedStringForItems := "[]VSHNRedisBackup{" + for _, f := range this.Items { + repeatedStringForItems += strings.Replace(strings.Replace(f.String(), "VSHNRedisBackup", "VSHNRedisBackup", 1), `&`, ``, 1) + "," + } + repeatedStringForItems += "}" + s := strings.Join([]string{`&VSHNRedisBackupList{`, + `ListMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ListMeta), "ListMeta", "v1.ListMeta", 1), `&`, ``, 1) + `,`, + `Items:` + repeatedStringForItems + `,`, + `}`, + }, "") + return s +} +func (this *VSHNRedisBackupStatus) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&VSHNRedisBackupStatus{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Date:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Date), "Time", "v1.Time", 1), `&`, ``, 1) + `,`, + `Instance:` + fmt.Sprintf("%v", this.Instance) + `,`, + `}`, + }, "") + return s +} +func (this *VSHNSize) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&VSHNSize{`, + `CPU:` + fmt.Sprintf("%v", this.CPU) + `,`, + `Disk:` + fmt.Sprintf("%v", this.Disk) + `,`, + `Memory:` + fmt.Sprintf("%v", this.Memory) + `,`, + `}`, + }, "") + return s +} +func valueToStringGenerated(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *AppCat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AppCat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AppCat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Details", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Details == nil { + m.Details = make(Details) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Details[mapkey] = mapvalue + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Plans", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Plans == nil { + m.Plans = make(map[string]VSHNPlan) + } + var mapkey string + mapvalue := &VSHNPlan{} + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthGenerated + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return ErrInvalidLengthGenerated + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &VSHNPlan{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Plans[mapkey] = *mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AppCatList) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AppCatList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AppCatList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Items = append(m.Items, AppCat{}) + if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AppCatStatus) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AppCatStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AppCatStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CompositionName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CompositionName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SGBackupInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SGBackupInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SGBackupInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Process", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Process.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BackupInformation", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.BackupInformation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *VSHNPlan) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VSHNPlan: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VSHNPlan: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Note", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Note = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field JSize", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.JSize.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *VSHNPostgresBackup) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VSHNPostgresBackup: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VSHNPostgresBackup: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *VSHNPostgresBackupList) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VSHNPostgresBackupList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VSHNPostgresBackupList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Items = append(m.Items, VSHNPostgresBackup{}) + if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *VSHNPostgresBackupStatus) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VSHNPostgresBackupStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VSHNPostgresBackupStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Process", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Process == nil { + m.Process = &runtime.RawExtension{} + } + if err := m.Process.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BackupInformation", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BackupInformation == nil { + m.BackupInformation = &runtime.RawExtension{} + } + if err := m.BackupInformation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DatabaseInstance", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DatabaseInstance = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *VSHNRedisBackup) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VSHNRedisBackup: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VSHNRedisBackup: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *VSHNRedisBackupList) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VSHNRedisBackupList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VSHNRedisBackupList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Items = append(m.Items, VSHNRedisBackup{}) + if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *VSHNRedisBackupStatus) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VSHNRedisBackupStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VSHNRedisBackupStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Date", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Date.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Instance", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Instance = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *VSHNSize) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VSHNSize: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VSHNSize: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CPU", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CPU = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Disk", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Disk = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Memory", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Memory = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenerated(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenerated + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenerated + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenerated + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenerated + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenerated + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenerated + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenerated = fmt.Errorf("proto: unexpected end of group") +) diff --git a/apis/appcat/v1/register.go b/apis/appcat/v1/register.go new file mode 100644 index 0000000..0dabe4c --- /dev/null +++ b/apis/appcat/v1/register.go @@ -0,0 +1,41 @@ +// Package v1 contains API Schema definitions for the appcat-server v1 API group +// +kubebuilder:object:generate=true +// +kubebuilder:skip +// +groupName=api.appcat.vshn.io +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "api.appcat.vshn.io", Version: "v1"} + + SchemeBuilder runtime.SchemeBuilder + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + SchemeBuilder.Register(addKnownTypes) +} + +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(GroupVersion, + &AppCat{}, + &AppCatList{}, + &VSHNPostgresBackup{}, + &VSHNPostgresBackupList{}, + &VSHNRedisBackup{}, + &VSHNRedisBackupList{}, + ) + metav1.AddToGroupVersion(scheme, GroupVersion) + return nil +} + +func GetGroupResource(resource string) schema.GroupResource { + return schema.GroupResource{Group: GroupVersion.Group, Resource: resource} +} diff --git a/apis/appcat/v1/vshn_postgres_backup_types.go b/apis/appcat/v1/vshn_postgres_backup_types.go new file mode 100644 index 0000000..153c005 --- /dev/null +++ b/apis/appcat/v1/vshn_postgres_backup_types.go @@ -0,0 +1,139 @@ +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/apiserver-runtime/pkg/builder/resource" +) + +var ( + // ResourceBackup is the name of this backup resource in plural form + ResourceBackup = "vshnpostgresbackups" + + // Metadata holds field path name metadata + Metadata = "metadata" + // Status holds field path name status + Status = "status" + // Process holds field path name process + Process = "process" + // BackupInformation holds field path name backupInformation + BackupInformation = "backupInformation" + // Timing holds field path name timing + Timing = "timing" + // End holds field path name end + End = "end" +) + +// VSHNPostgreSQLName represents the name of a VSHNPostgreSQL +type VSHNPostgreSQLName string + +// VSHNPostgreSQLNamespace represents the namespace of a VSHNPostgreSQL +type VSHNPostgreSQLNamespace string + +// +kubebuilder:object:root=true + +// VSHNPostgresBackup defines VSHN managed PostgreSQL backups +type VSHNPostgresBackup struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Status holds the backup specific metadata. + Status VSHNPostgresBackupStatus `json:"status,omitempty"` +} + +// VSHNPostgresBackupStatus defines the desired state of VSHNPostgresBackup +type VSHNPostgresBackupStatus struct { + // Process holds status information of the backup process + Process *runtime.RawExtension `json:"process,omitempty"` + // BackupInformation holds specific backup information + BackupInformation *runtime.RawExtension `json:"backupInformation,omitempty"` + // DatabaseInstance is the database from which the backup has been done + DatabaseInstance string `json:"databaseInstance"` +} + +// VSHNPostgresBackup needs to implement the builder resource interface +var _ resource.Object = &VSHNPostgresBackup{} + +func (in *VSHNPostgresBackup) GetObjectMeta() *metav1.ObjectMeta { + return &in.ObjectMeta +} + +func (in *VSHNPostgresBackup) NamespaceScoped() bool { + return true +} + +func (in *VSHNPostgresBackup) New() runtime.Object { + return &VSHNPostgresBackup{} +} + +func (in *VSHNPostgresBackup) NewList() runtime.Object { + return &VSHNPostgresBackupList{} +} + +// GetGroupVersionResource returns the GroupVersionResource for this resource. +// The resource should be the all lowercase and pluralized kind +func (in *VSHNPostgresBackup) GetGroupVersionResource() schema.GroupVersionResource { + return schema.GroupVersionResource{ + Group: GroupVersion.Group, + Version: GroupVersion.Version, + Resource: "vshnpostgresbackups", + } +} + +// IsStorageVersion returns true if the object is also the internal version -- i.e. is the type defined for the API group or an alias to this object. +// If false, the resource is expected to implement MultiVersionObject interface. +func (in *VSHNPostgresBackup) IsStorageVersion() bool { + return true +} + +// +kubebuilder:object:root=true + +// VSHNPostgresBackupList defines a list of VSHNPostgresBackup +type VSHNPostgresBackupList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []VSHNPostgresBackup `json:"items"` +} + +var _ resource.ObjectList = &VSHNPostgresBackupList{} + +func (in *VSHNPostgresBackupList) GetListMeta() *metav1.ListMeta { + return &in.ListMeta +} + +func New() *VSHNPostgresBackup { + return &VSHNPostgresBackup{} +} + +// SGBackupInfo holds necessary data for VSHNPostgresBackup +type SGBackupInfo struct { + metav1.ObjectMeta + Process runtime.RawExtension + BackupInformation runtime.RawExtension +} + +// NewVSHNPostgresBackup creates a new VSHNPostgresBackup out of a SGBackupInfo and a database instance +func NewVSHNPostgresBackup(backup *SGBackupInfo, db, originalNamespace string) *VSHNPostgresBackup { + if backup == nil || originalNamespace == "" || db == "" { + return nil + } + + vshnPostgresBackup := &VSHNPostgresBackup{ + ObjectMeta: backup.ObjectMeta, + } + + vshnPostgresBackup.Status.DatabaseInstance = db + vshnPostgresBackup.Namespace = originalNamespace + + if backup.Process.Object != nil { + vshnPostgresBackup.Status.Process = &backup.Process + } + + if backup.BackupInformation.Object != nil { + vshnPostgresBackup.Status.BackupInformation = &backup.BackupInformation + } + + return vshnPostgresBackup +} diff --git a/apis/appcat/v1/vshn_postgres_backup_types_test.go b/apis/appcat/v1/vshn_postgres_backup_types_test.go new file mode 100644 index 0000000..1147972 --- /dev/null +++ b/apis/appcat/v1/vshn_postgres_backup_types_test.go @@ -0,0 +1,113 @@ +package v1 + +import ( + "gotest.tools/v3/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "testing" +) + +func TestNewVSHNBackupFromBackInfo(t *testing.T) { + tests := map[string]struct { + db string + namespace string + backupInfo *SGBackupInfo + vshnPostgresBackup *VSHNPostgresBackup + }{ + "GivenNoBackupInfo_ThenNil": { + db: "db1", + namespace: "namespace", + }, + "GivenNoDB_ThenNil": { + backupInfo: &SGBackupInfo{ + ObjectMeta: metav1.ObjectMeta{Name: "backup"}, + Process: *rawFromObject(getTestProcess()), + BackupInformation: *rawFromObject(getTestBI()), + }, + namespace: "namespace", + }, + "GivenNoNamespace_ThenNil": { + backupInfo: &SGBackupInfo{ + ObjectMeta: metav1.ObjectMeta{Name: "backup"}, + Process: *rawFromObject(getTestProcess()), + BackupInformation: *rawFromObject(getTestBI()), + }, + db: "db1", + }, + "GivenBackupInfo_ThenVSHNBackup": { + db: "db1", + namespace: "namespace", + backupInfo: &SGBackupInfo{ + ObjectMeta: metav1.ObjectMeta{Name: "backup"}, + Process: *rawFromObject(getTestProcess()), + BackupInformation: *rawFromObject(getTestBI()), + }, + vshnPostgresBackup: &VSHNPostgresBackup{ + ObjectMeta: metav1.ObjectMeta{Name: "backup", Namespace: "namespace"}, + Status: VSHNPostgresBackupStatus{ + DatabaseInstance: "db1", + Process: rawFromObject(getTestProcess()), + BackupInformation: rawFromObject(getTestBI()), + }, + }, + }, + "GivenBackupInfoWithNoProcess_ThenVSHNBackup": { + db: "db1", + namespace: "namespace", + backupInfo: &SGBackupInfo{ + ObjectMeta: metav1.ObjectMeta{Name: "backup"}, + BackupInformation: *rawFromObject(getTestBI()), + }, + vshnPostgresBackup: &VSHNPostgresBackup{ + ObjectMeta: metav1.ObjectMeta{Name: "backup", Namespace: "namespace"}, + Status: VSHNPostgresBackupStatus{ + DatabaseInstance: "db1", + BackupInformation: rawFromObject(getTestBI()), + }, + }, + }, + "GivenBackupInfoWithNoBI_ThenVSHNBackup": { + db: "db1", + namespace: "namespace", + backupInfo: &SGBackupInfo{ + ObjectMeta: metav1.ObjectMeta{Name: "backup"}, + Process: *rawFromObject(getTestProcess()), + }, + vshnPostgresBackup: &VSHNPostgresBackup{ + ObjectMeta: metav1.ObjectMeta{Name: "backup", Namespace: "namespace"}, + Status: VSHNPostgresBackupStatus{ + DatabaseInstance: "db1", + Process: rawFromObject(getTestProcess()), + }, + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + actualBackup := NewVSHNPostgresBackup(tt.backupInfo, tt.db, tt.namespace) + assert.DeepEqual(t, tt.vshnPostgresBackup, actualBackup) + }) + } +} + +func getTestProcess() unstructured.Unstructured { + return unstructured.Unstructured{ + Object: map[string]interface{}{ + "status": "Completed", + }, + } +} + +func getTestBI() unstructured.Unstructured { + return unstructured.Unstructured{ + Object: map[string]interface{}{ + "memory": 1024, + "time": "45s", + }, + } +} + +func rawFromObject(object unstructured.Unstructured) *runtime.RawExtension { + return &runtime.RawExtension{Object: &object} +} diff --git a/apis/appcat/v1/vshn_redis_backup_types.go b/apis/appcat/v1/vshn_redis_backup_types.go new file mode 100644 index 0000000..a482156 --- /dev/null +++ b/apis/appcat/v1/vshn_redis_backup_types.go @@ -0,0 +1,72 @@ +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/apiserver-runtime/pkg/builder/resource" +) + +// VSHNRedisBackup needs to implement the builder resource interface +var _ resource.Object = &VSHNRedisBackup{} +var _ resource.ObjectList = &VSHNRedisBackupList{} + +// +kubebuilder:object:root=true + +type VSHNRedisBackup struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Status VSHNRedisBackupStatus `json:"status,omitempty"` +} + +type VSHNRedisBackupStatus struct { + ID string `json:"id,omitempty"` + Date metav1.Time `json:"date,omitempty"` + Instance string `json:"instance,omitempty"` +} + +// +kubebuilder:object:root=true + +type VSHNRedisBackupList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []VSHNRedisBackup `json:"items,omitempty"` +} + +// GetGroupVersionResource returns the GroupVersionResource for this resource. +// The resource should be the all lowercase and pluralized kind +func (in *VSHNRedisBackup) GetGroupVersionResource() schema.GroupVersionResource { + return schema.GroupVersionResource{ + Group: GroupVersion.Group, + Version: GroupVersion.Version, + Resource: "vshnredisbackups", + } +} + +func (in *VSHNRedisBackup) GetObjectMeta() *metav1.ObjectMeta { + return &in.ObjectMeta +} + +// IsStorageVersion returns true if the object is also the internal version -- i.e. is the type defined for the API group or an alias to this object. +// If false, the resource is expected to implement MultiVersionObject interface. +func (in *VSHNRedisBackup) IsStorageVersion() bool { + return true +} + +func (in *VSHNRedisBackup) NamespaceScoped() bool { + return true +} + +func (in *VSHNRedisBackup) New() runtime.Object { + return &VSHNRedisBackup{} +} + +func (in *VSHNRedisBackup) NewList() runtime.Object { + return &VSHNRedisBackupList{} +} + +func (in *VSHNRedisBackupList) GetListMeta() *metav1.ListMeta { + return &in.ListMeta +} diff --git a/apis/appcat/v1/zz_generated.deepcopy.go b/apis/appcat/v1/zz_generated.deepcopy.go new file mode 100644 index 0000000..3f57d98 --- /dev/null +++ b/apis/appcat/v1/zz_generated.deepcopy.go @@ -0,0 +1,324 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v1 + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AppCat) DeepCopyInto(out *AppCat) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Details != nil { + in, out := &in.Details, &out.Details + *out = make(Details, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Plans != nil { + in, out := &in.Plans, &out.Plans + *out = make(map[string]VSHNPlan, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppCat. +func (in *AppCat) DeepCopy() *AppCat { + if in == nil { + return nil + } + out := new(AppCat) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AppCat) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AppCatList) DeepCopyInto(out *AppCatList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]AppCat, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppCatList. +func (in *AppCatList) DeepCopy() *AppCatList { + if in == nil { + return nil + } + out := new(AppCatList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AppCatList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AppCatStatus) DeepCopyInto(out *AppCatStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppCatStatus. +func (in *AppCatStatus) DeepCopy() *AppCatStatus { + if in == nil { + return nil + } + out := new(AppCatStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in Details) DeepCopyInto(out *Details) { + { + in := &in + *out = make(Details, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Details. +func (in Details) DeepCopy() Details { + if in == nil { + return nil + } + out := new(Details) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SGBackupInfo) DeepCopyInto(out *SGBackupInfo) { + *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Process.DeepCopyInto(&out.Process) + in.BackupInformation.DeepCopyInto(&out.BackupInformation) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SGBackupInfo. +func (in *SGBackupInfo) DeepCopy() *SGBackupInfo { + if in == nil { + return nil + } + out := new(SGBackupInfo) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPlan) DeepCopyInto(out *VSHNPlan) { + *out = *in + out.JSize = in.JSize +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPlan. +func (in *VSHNPlan) DeepCopy() *VSHNPlan { + if in == nil { + return nil + } + out := new(VSHNPlan) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgresBackup) DeepCopyInto(out *VSHNPostgresBackup) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgresBackup. +func (in *VSHNPostgresBackup) DeepCopy() *VSHNPostgresBackup { + if in == nil { + return nil + } + out := new(VSHNPostgresBackup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VSHNPostgresBackup) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgresBackupList) DeepCopyInto(out *VSHNPostgresBackupList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VSHNPostgresBackup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgresBackupList. +func (in *VSHNPostgresBackupList) DeepCopy() *VSHNPostgresBackupList { + if in == nil { + return nil + } + out := new(VSHNPostgresBackupList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VSHNPostgresBackupList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgresBackupStatus) DeepCopyInto(out *VSHNPostgresBackupStatus) { + *out = *in + if in.Process != nil { + in, out := &in.Process, &out.Process + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) + } + if in.BackupInformation != nil { + in, out := &in.BackupInformation, &out.BackupInformation + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgresBackupStatus. +func (in *VSHNPostgresBackupStatus) DeepCopy() *VSHNPostgresBackupStatus { + if in == nil { + return nil + } + out := new(VSHNPostgresBackupStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisBackup) DeepCopyInto(out *VSHNRedisBackup) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisBackup. +func (in *VSHNRedisBackup) DeepCopy() *VSHNRedisBackup { + if in == nil { + return nil + } + out := new(VSHNRedisBackup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VSHNRedisBackup) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisBackupList) DeepCopyInto(out *VSHNRedisBackupList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VSHNRedisBackup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisBackupList. +func (in *VSHNRedisBackupList) DeepCopy() *VSHNRedisBackupList { + if in == nil { + return nil + } + out := new(VSHNRedisBackupList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VSHNRedisBackupList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisBackupStatus) DeepCopyInto(out *VSHNRedisBackupStatus) { + *out = *in + in.Date.DeepCopyInto(&out.Date) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisBackupStatus. +func (in *VSHNRedisBackupStatus) DeepCopy() *VSHNRedisBackupStatus { + if in == nil { + return nil + } + out := new(VSHNRedisBackupStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNSize) DeepCopyInto(out *VSHNSize) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNSize. +func (in *VSHNSize) DeepCopy() *VSHNSize { + if in == nil { + return nil + } + out := new(VSHNSize) + in.DeepCopyInto(out) + return out +} diff --git a/apis/v1/conditions.go b/apis/v1/conditions.go new file mode 100644 index 0000000..9e913a7 --- /dev/null +++ b/apis/v1/conditions.go @@ -0,0 +1,48 @@ +package v1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// The reason we need a custom condition type is that metav1.Condition declares all fields as required. +// However, we have seen issues where Crossplane's properties in Conditions aren't all required and lead to Crossplane not being able to copy conditions. + +type Condition struct { + // +kubebuilder:validation:Optional + // +kubebuilder:validation:Pattern=`^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$` + // +kubebuilder:validation:MaxLength=316 + + // Type of condition. + Type string `json:"type,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:validation:Enum=True;False;Unknown + + // Status of the condition, one of True, False, Unknown. + Status metav1.ConditionStatus `json:"status,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:validation:Minimum=0 + + // ObservedGeneration represents the .metadata.generation that the condition was set based upon. + // For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Format=date-time + + // LastTransitionTime is the last time the condition transitioned from one status to another. + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:validation:MaxLength=1024 + // +kubebuilder:validation:Pattern=`^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$` + + // Reason contains a programmatic identifier indicating the reason for the condition's last transition. + Reason string `json:"reason,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:validation:MaxLength=32768 + + // Message is a human-readable message indicating details about the transition. + Message string `json:"message,omitempty"` +} diff --git a/apis/v1/groupversion_info.go b/apis/v1/groupversion_info.go new file mode 100644 index 0000000..f71dd72 --- /dev/null +++ b/apis/v1/groupversion_info.go @@ -0,0 +1,25 @@ +// +kubebuilder:object:generate=true +// +groupName=appcat.vshn.io +// +versionName=v1 + +package v1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "appcat.vshn.io", Version: "v1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) + +func init() { + SchemeBuilder.Register(&ObjectBucket{}, &XObjectBucket{}) +} diff --git a/apis/v1/objectreference.go b/apis/v1/objectreference.go new file mode 100644 index 0000000..939097f --- /dev/null +++ b/apis/v1/objectreference.go @@ -0,0 +1,13 @@ +package v1 + +// We use a custom LocalObjectReference as the description of corev1.LocalObjectReference contains a todo + +// LocalObjectReference contains enough information to let you locate the +// referenced object inside the same namespace. +// +structType=atomic +type LocalObjectReference struct { + // Name of the referent. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"` + Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,opt,name=namespace"` +} diff --git a/apis/v1/objectstorage_types.go b/apis/v1/objectstorage_types.go new file mode 100644 index 0000000..2f5b2ff --- /dev/null +++ b/apis/v1/objectstorage_types.go @@ -0,0 +1,77 @@ +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +// +kubebuilder:printcolumn:name="Bucket Name",type="string",JSONPath=".spec.parameters.bucketName" +// +kubebuilder:printcolumn:name="Region",type="string",JSONPath=".spec.parameters.region" + +// ObjectBucket is the API for creating S3 buckets. +type ObjectBucket struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ObjectBucketSpec `json:"spec"` + Status ObjectBucketStatus `json:"status,omitempty"` +} + +// ObjectBucketSpec defines the desired state of a ObjectBucket. +type ObjectBucketSpec struct { + Parameters ObjectBucketParameters `json:"parameters,omitempty"` + + // WriteConnectionSecretToRef references a secret to which the connection details will be written. + WriteConnectionSecretToRef LocalObjectReference `json:"writeConnectionSecretToRef,omitempty"` +} + +// ObjectBucketParameters are the configurable fields of a ObjectBucket. +type ObjectBucketParameters struct { + // +kubebuilder:validation:Required + + // BucketName is the name of the bucket to create. + // Cannot be changed after bucket is created. + // Name must be acceptable by the S3 protocol, which follows RFC 1123. + // Be aware that S3 providers may require a unique name across the platform or region. + BucketName string `json:"bucketName"` + + // +kubebuilder:validation:Required + + // Region is the name of the region where the bucket shall be created. + // The region must be available in the S3 endpoint. + Region string `json:"region"` +} + +// ObjectBucketStatus reflects the observed state of a ObjectBucket. +type ObjectBucketStatus struct { + // AccessUserConditions contains a copy of the claim's underlying user account conditions. + AccessUserConditions []Condition `json:"accessUserConditions,omitempty"` + // BucketConditions contains a copy of the claim's underlying bucket conditions. + BucketConditions []Condition `json:"bucketConditions,omitempty"` +} + +// +kubebuilder:object:generate=true +// +kubebuilder:object:root=true + +// XObjectBucket represents the internal composite of this claim +type XObjectBucket struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec XObjectBucketSpec `json:"spec"` + Status ObjectBucketStatus `json:"status,omitempty"` +} + +// XObjectBucketSpec defines the desired state of a ObjectBucket. +type XObjectBucketSpec struct { + Parameters ObjectBucketParameters `json:"parameters,omitempty"` + + // WriteConnectionSecretToRef references a secret to which the connection details will be written. + WriteConnectionSecretToRef NamespacedName `json:"writeConnectionSecretToRef,omitempty"` +} + +// NamespacedName describes an object reference by its name and namespace +type NamespacedName struct { + Namespace string `json:"namespace,omitempty"` + Name string `json:"name,omitempty"` +} diff --git a/apis/v1/zz_generated.deepcopy.go b/apis/v1/zz_generated.deepcopy.go new file mode 100644 index 0000000..dbe78c3 --- /dev/null +++ b/apis/v1/zz_generated.deepcopy.go @@ -0,0 +1,188 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Condition) DeepCopyInto(out *Condition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalObjectReference) DeepCopyInto(out *LocalObjectReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalObjectReference. +func (in *LocalObjectReference) DeepCopy() *LocalObjectReference { + if in == nil { + return nil + } + out := new(LocalObjectReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NamespacedName) DeepCopyInto(out *NamespacedName) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespacedName. +func (in *NamespacedName) DeepCopy() *NamespacedName { + if in == nil { + return nil + } + out := new(NamespacedName) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectBucket) DeepCopyInto(out *ObjectBucket) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectBucket. +func (in *ObjectBucket) DeepCopy() *ObjectBucket { + if in == nil { + return nil + } + out := new(ObjectBucket) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ObjectBucket) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectBucketParameters) DeepCopyInto(out *ObjectBucketParameters) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectBucketParameters. +func (in *ObjectBucketParameters) DeepCopy() *ObjectBucketParameters { + if in == nil { + return nil + } + out := new(ObjectBucketParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectBucketSpec) DeepCopyInto(out *ObjectBucketSpec) { + *out = *in + out.Parameters = in.Parameters + out.WriteConnectionSecretToRef = in.WriteConnectionSecretToRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectBucketSpec. +func (in *ObjectBucketSpec) DeepCopy() *ObjectBucketSpec { + if in == nil { + return nil + } + out := new(ObjectBucketSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectBucketStatus) DeepCopyInto(out *ObjectBucketStatus) { + *out = *in + if in.AccessUserConditions != nil { + in, out := &in.AccessUserConditions, &out.AccessUserConditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.BucketConditions != nil { + in, out := &in.BucketConditions, &out.BucketConditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectBucketStatus. +func (in *ObjectBucketStatus) DeepCopy() *ObjectBucketStatus { + if in == nil { + return nil + } + out := new(ObjectBucketStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *XObjectBucket) DeepCopyInto(out *XObjectBucket) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new XObjectBucket. +func (in *XObjectBucket) DeepCopy() *XObjectBucket { + if in == nil { + return nil + } + out := new(XObjectBucket) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *XObjectBucket) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *XObjectBucketSpec) DeepCopyInto(out *XObjectBucketSpec) { + *out = *in + out.Parameters = in.Parameters + out.WriteConnectionSecretToRef = in.WriteConnectionSecretToRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new XObjectBucketSpec. +func (in *XObjectBucketSpec) DeepCopy() *XObjectBucketSpec { + if in == nil { + return nil + } + out := new(XObjectBucketSpec) + in.DeepCopyInto(out) + return out +} diff --git a/apis/vshn/v1/common_types.go b/apis/vshn/v1/common_types.go new file mode 100644 index 0000000..b151edb --- /dev/null +++ b/apis/vshn/v1/common_types.go @@ -0,0 +1,84 @@ +package v1 + +// K8upBackupSpec specifies when a backup for redis should be triggered. +// It also contains the retention policy for the backup. +type K8upBackupSpec struct { + // +kubebuilder:validation:Pattern=^(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])) (\*|([0-9]|1[0-9]|2[0-3])|\*\/([0-9]|1[0-9]|2[0-3])) (\*|([1-9]|1[0-9]|2[0-9]|3[0-1])|\*\/([1-9]|1[0-9]|2[0-9]|3[0-1])) (\*|([1-9]|1[0-2])|\*\/([1-9]|1[0-2])) (\*|([0-6])|\*\/([0-6]))$ + Schedule string `json:"schedule,omitempty"` + + Retention K8upRetentionPolicy `json:"retention,omitempty"` +} + +// GetBackupSchedule returns the currently set schedule for this backup config +func (k *K8upBackupSpec) GetBackupSchedule() string { + return k.Schedule +} + +// SetBackupSchedule sets the schedule to the given value +func (k *K8upBackupSpec) SetBackupSchedule(schedule string) { + k.Schedule = schedule +} + +// K8upRetentionPolicy describes the retention configuration for a K8up backup. +type K8upRetentionPolicy struct { + KeepLast int `json:"keepLast,omitempty"` + KeepHourly int `json:"keepHourly,omitempty"` + // +kubebuilder:default=6 + KeepDaily int `json:"keepDaily,omitempty"` + KeepWeekly int `json:"keepWeekly,omitempty"` + KeepMonthly int `json:"keepMonthly,omitempty"` + KeepYearly int `json:"keepYearly,omitempty"` +} + +// K8upRestoreSpec contains restore specific parameters. +type K8upRestoreSpec struct { + + // ClaimName specifies the name of the instance you want to restore from. + // The claim has to be in the same namespace as this new instance. + ClaimName string `json:"claimName,omitempty"` + + // BackupName is the name of the specific backup you want to restore. + BackupName string `json:"backupName,omitempty"` +} + +type VSHNDBaaSServiceLevel string + +const ( + BestEffort VSHNDBaaSServiceLevel = "besteffort" + Guaranteed VSHNDBaaSServiceLevel = "guaranteed" +) + +// VSHNDBaaSMaintenanceScheduleSpec contains settings to control the maintenance of an instance. +type VSHNDBaaSMaintenanceScheduleSpec struct { + // +kubebuilder:validation:Enum=monday;tuesday;wednesday;thursday;friday;saturday;sunday + + // DayOfWeek specifies at which weekday the maintenance is held place. + // Allowed values are [monday, tuesday, wednesday, thursday, friday, saturday, sunday] + DayOfWeek string `json:"dayOfWeek,omitempty"` + + // +kubebuilder:validation:Pattern="^([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$" + + // TimeOfDay for installing updates in UTC. + // Format: "hh:mm:ss". + TimeOfDay string `json:"timeOfDay,omitempty"` +} + +// GetMaintenanceDayOfWeek returns the currently set day of week +func (n *VSHNDBaaSMaintenanceScheduleSpec) GetMaintenanceDayOfWeek() string { + return n.DayOfWeek +} + +// GetMaintenanceTimeOfDay returns the currently set time of day +func (n *VSHNDBaaSMaintenanceScheduleSpec) GetMaintenanceTimeOfDay() string { + return n.TimeOfDay +} + +// SetMaintenanceDayOfWeek sets the day of week to the given value +func (n *VSHNDBaaSMaintenanceScheduleSpec) SetMaintenanceDayOfWeek(dow string) { + n.DayOfWeek = dow +} + +// SetMaintenanceTimeOfDay sets the time of day to the given value +func (n *VSHNDBaaSMaintenanceScheduleSpec) SetMaintenanceTimeOfDay(tod string) { + n.TimeOfDay = tod +} diff --git a/apis/vshn/v1/dbaas_vshn_postgresql.go b/apis/vshn/v1/dbaas_vshn_postgresql.go new file mode 100644 index 0000000..1953372 --- /dev/null +++ b/apis/vshn/v1/dbaas_vshn_postgresql.go @@ -0,0 +1,327 @@ +package v1 + +import ( + alertmanagerv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1" + apisv1 "github.com/vshn/appcat-apiserver/apis/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// Workaround to make nested defaulting work. +// kubebuilder is unable to set a {} default +//go:generate yq -i e ../../generated/vshn.appcat.vshn.io_vshnpostgresqls.yaml --expression "with(.spec.versions[]; .schema.openAPIV3Schema.properties.spec.properties.parameters.default={})" +//go:generate yq -i e ../../generated/vshn.appcat.vshn.io_vshnpostgresqls.yaml --expression "with(.spec.versions[]; .schema.openAPIV3Schema.properties.spec.properties.parameters.properties.size.default={})" +//go:generate yq -i e ../../generated/vshn.appcat.vshn.io_vshnpostgresqls.yaml --expression "with(.spec.versions[]; .schema.openAPIV3Schema.properties.spec.properties.parameters.properties.service.default={})" +//go:generate yq -i e ../../generated/vshn.appcat.vshn.io_vshnpostgresqls.yaml --expression "with(.spec.versions[]; .schema.openAPIV3Schema.properties.spec.properties.parameters.properties.backup.default={})" +//go:generate yq -i e ../../generated/vshn.appcat.vshn.io_vshnpostgresqls.yaml --expression "with(.spec.versions[]; .schema.openAPIV3Schema.properties.spec.properties.parameters.properties.maintenance.default={})" + +// +kubebuilder:object:root=true + +// VSHNPostgreSQL is the API for creating Postgresql clusters. +type VSHNPostgreSQL struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of a VSHNPostgreSQL. + Spec VSHNPostgreSQLSpec `json:"spec"` + + // Status reflects the observed state of a VSHNPostgreSQL. + Status VSHNPostgreSQLStatus `json:"status,omitempty"` +} + +// VSHNPostgreSQLSpec defines the desired state of a VSHNPostgreSQL. +type VSHNPostgreSQLSpec struct { + // Parameters are the configurable fields of a VSHNPostgreSQL. + Parameters VSHNPostgreSQLParameters `json:"parameters,omitempty"` + // WriteConnectionSecretToRef references a secret to which the connection details will be written. + WriteConnectionSecretToRef apisv1.LocalObjectReference `json:"writeConnectionSecretToRef,omitempty"` + + // ResourceRef contains a reference to the composite. + ResourceRef corev1.ObjectReference `json:"resourceRef,omitempty"` + + // CompositeDeletePolicy defines how the claim should behave if it's deleted. + // This field definition will be overwritten by crossplane again, once the XRD is applied to a cluster. + // It's added here so it can be marshalled correctly in third party operators or composition functions. + CompositeDeletePolicy string `json:"compositeDeletePolicy,omitempty"` +} + +// VSHNPostgreSQLParameters are the configurable fields of a VSHNPostgreSQL. +type VSHNPostgreSQLParameters struct { + // Service contains PostgreSQL DBaaS specific properties + Service VSHNPostgreSQLServiceSpec `json:"service,omitempty"` + + // Maintenance contains settings to control the maintenance of an instance. + Maintenance VSHNDBaaSMaintenanceScheduleSpec `json:"maintenance,omitempty"` + + // Size contains settings to control the sizing of a service. + Size VSHNDBaaSSizeSpec `json:"size,omitempty"` + + // Scheduling contains settings to control the scheduling of an instance. + Scheduling VSHNDBaaSSchedulingSpec `json:"scheduling,omitempty"` + + // Network contains any network related settings. + Network VSHNDBaaSNetworkSpec `json:"network,omitempty"` + + // Backup contains settings to control the backups of an instance. + Backup VSHNPostgreSQLBackup `json:"backup,omitempty"` + + // Restore contains settings to control the restore of an instance. + Restore VSHNPostgreSQLRestore `json:"restore,omitempty"` + + // Monitoring contains settings to control monitoring. + Monitoring VSHNPostgreSQLMonitoring `json:"monitoring,omitempty"` + + // Encryption contains settings to control the storage encryption of an instance. + Encryption VSHNPostgreSQLEncryption `json:"encryption,omitempty"` + + // UpdateStrategy indicates when updates to the instance spec will be applied. + UpdateStrategy VSHNPostgreSQLUpdateStrategy `json:"updateStrategy,omitempty"` + + // +kubebuilder:default=1 + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=3 + + // Instances configures the number of PostgreSQL instances for the cluster. + // Each instance contains one Postgres server. + // Out of all Postgres servers, one is elected as the primary, the rest remain as read-only replicas. + Instances int `json:"instances,omitempty"` + + // This section allows to configure Postgres replication mode and HA roles groups. + // + // The main replication group is implicit and contains the total number of instances less the sum of all instances in other replication groups. + Replication VSHNPostgreSQLReplicationStrategy `json:"replication,omitempty"` +} + +type VSHNPostgreSQLReplicationStrategy struct { + // +kubebuilder:validation:Enum="async";"sync";"strict-sync" + + // Mode defines the replication mode applied to the whole cluster. Possible values are: "async"(default), "sync", and "strict-sync" + // + // "async": When in asynchronous mode the cluster is allowed to lose some committed transactions. + // When the primary server fails or becomes unavailable for any other reason a sufficiently healthy standby will automatically be promoted to primary. + // Any transactions that have not been replicated to that standby remain in a “forked timeline” on the primary, and are effectively unrecoverable + // + // "sync": When in synchronous mode a standby will not be promoted unless it is certain that the standby contains all transactions that may have returned a successful commit status to client. + // This means that the system may be unavailable for writes even though some servers are available. + // + // "strict-sync": When it is absolutely necessary to guarantee that each write is stored durably on at least two nodes, use the strict synchronous mode. + // This mode prevents synchronous replication to be switched off on the primary when no synchronous standby candidates are available. + // As a downside, the primary will not be available for writes, blocking all client write requests until at least one synchronous replica comes up. + // + // NOTE: We recommend to always use three intances when setting the mode to "strict-sync". + Mode string `json:"mode,omitempty"` +} + +const VSHNPostgreSQLUpdateStrategyTypeImmediate = "Immediate" +const VSHNPostgreSQLUpdateStrategyTypeOnRestart = "OnRestart" + +// VSHNPostgreSQLUpdateStrategy indicates how and when updates to the instance spec will be applied. +type VSHNPostgreSQLUpdateStrategy struct { + // +kubebuilder:validation:Enum="Immediate";"OnRestart" + // +kubebuilder:default="Immediate" + + // Type indicates the type of the UpdateStrategy. Default is OnRestart. + // Possible enum values: + // - `"OnRestart"` indicates that the changes to the spec will only be applied once the instance is restarted by other means, most likely during maintenance. + // - `"Immediate"` indicates that update will be applied to the instance as soon as the spec changes. Please be aware that this might lead to short downtime. + Type string `json:"type,omitempty"` +} + +// VSHNPostgreSQLServiceSpec contains PostgreSQL DBaaS specific properties +type VSHNPostgreSQLServiceSpec struct { + // +kubebuilder:validation:Enum="12";"13";"14";"15" + // +kubebuilder:default="15" + + // MajorVersion contains supported version of PostgreSQL. + // Multiple versions are supported. The latest version "15" is the default version. + MajorVersion string `json:"majorVersion,omitempty"` + + // PGSettings contains additional PostgreSQL settings. + PostgreSQLSettings runtime.RawExtension `json:"pgSettings,omitempty"` + + // Extensions allow to enable/disable any of the supported + Extensions []VSHNDBaaSPostgresExtension `json:"extensions,omitempty"` + + // +kubebuilder:validation:Enum="besteffort";"guaranteed" + // +kubebuilder:default="besteffort" + + // ServiceLevel defines the service level of this service. Either Best Effort or Guaranteed Availability is allowed. + ServiceLevel VSHNDBaaSServiceLevel `json:"serviceLevel,omitempty"` +} + +// VSHNDBaaSPostgresExtension contains the name of a single extension. +type VSHNDBaaSPostgresExtension struct { + // Name is the name of the extension to enable. + // For an extensive list, please consult https://stackgres.io/doc/latest/intro/extensions/ + Name string `json:"name,omitempty"` +} + +// VSHNDBaaSSchedulingSpec contains settings to control the scheduling of an instance. +type VSHNDBaaSSchedulingSpec struct { + // NodeSelector is a selector which must match a node’s labels for the pod to be scheduled on that node + NodeSelector map[string]string `json:"nodeSelector,omitempty"` +} + +// VSHNDBaaSSizeSpec contains settings to control the sizing of a service. +type VSHNDBaaSSizeSpec struct { + // CPU defines the amount of Kubernetes CPUs for an instance. + CPU string `json:"cpu,omitempty"` + + // Memory defines the amount of memory in units of bytes for an instance. + Memory string `json:"memory,omitempty"` + + // Requests defines CPU and memory requests for an instance + Requests VSHNDBaaSSizeRequestsSpec `json:"requests,omitempty"` + + // Disk defines the amount of disk space for an instance. + Disk string `json:"disk,omitempty"` + + // Plan is the name of the resource plan that defines the compute resources. + Plan string `json:"plan,omitempty"` +} + +// VSHNDBaaSSizeRequestsSpec contains settings to control the resoure requests of a service. +type VSHNDBaaSSizeRequestsSpec struct { + // CPU defines the amount of Kubernetes CPUs for an instance. + CPU string `json:"cpu,omitempty"` + + // Memory defines the amount of memory in units of bytes for an instance. + Memory string `json:"memory,omitempty"` +} + +// VSHNDBaaSNetworkSpec contains any network related settings. +type VSHNDBaaSNetworkSpec struct { + // +kubebuilder:default={"0.0.0.0/0"} + + // IPFilter is a list of allowed IPv4 CIDR ranges that can access the service. + // If no IP Filter is set, you may not be able to reach the service. + // A value of `0.0.0.0/0` will open the service to all addresses on the public internet. + IPFilter []string `json:"ipFilter,omitempty"` + + // ServiceType defines the type of the service. + // Possible enum values: + // - `"ClusterIP"` indicates that the service is only reachable from within the cluster. + // - `"LoadBalancer"` indicates that the service is reachable from the public internet via dedicated Ipv4 address. + // +kubebuilder:default="ClusterIP" + // +kubebuilder:validation:Enum="ClusterIP";"LoadBalancer" + ServiceType string `json:"serviceType,omitempty"` +} + +type VSHNPostgreSQLBackup struct { + // +kubebuilder:validation:Pattern=^(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])) (\*|([0-9]|1[0-9]|2[0-3])|\*\/([0-9]|1[0-9]|2[0-3])) (\*|([1-9]|1[0-9]|2[0-9]|3[0-1])|\*\/([1-9]|1[0-9]|2[0-9]|3[0-1])) (\*|([1-9]|1[0-2])|\*\/([1-9]|1[0-2])) (\*|([0-6])|\*\/([0-6]))$ + Schedule string `json:"schedule,omitempty"` + + // +kubebuilder:validation:Pattern="^[1-9][0-9]*$" + // +kubebuilder:default=6 + // +kubebuilder:validation:XIntOrString + Retention int `json:"retention,omitempty"` + + // DeletionProtection will protect the instance from being deleted for the given retention time. + // This is enabled by default. + // +kubebuilder:default=true + DeletionProtection bool `json:"deletionProtection,omitempty"` + + // DeletionRetention specifies in days how long the instance should be kept after deletion. + // The default is keeping it one week. + // +kubebuilder:default=7 + DeletionRetention int `json:"deletionRetention,omitempty"` +} + +// GetBackupSchedule gets the currently set schedule +func (p *VSHNPostgreSQLBackup) GetBackupSchedule() string { + return p.Schedule +} + +// SetBackupSchedule sets the schedule to the given value +func (p *VSHNPostgreSQLBackup) SetBackupSchedule(schedule string) { + p.Schedule = schedule +} + +// VSHNPostgreSQLRestore contains restore specific parameters. +type VSHNPostgreSQLRestore struct { + + // ClaimName specifies the name of the instance you want to restore from. + // The claim has to be in the same namespace as this new instance. + ClaimName string `json:"claimName,omitempty"` + + // BackupName is the name of the specific backup you want to restore. + BackupName string `json:"backupName,omitempty"` + + // RecoveryTimeStamp an ISO 8601 date, that holds UTC date indicating at which point-in-time the database has to be restored. + // This is optional and if no PIT recovery is required, it can be left empty. + // +kubebuilder:validation:Pattern=`^(?:[1-9]\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:Z|[+-][01]\d:[0-5]\d)$` + RecoveryTimeStamp string `json:"recoveryTimeStamp,omitempty"` +} + +// VSHNPostgreSQLMonitoring contains settings to configure monitoring aspects of PostgreSQL +type VSHNPostgreSQLMonitoring struct { + // AlertmanagerConfigRef contains the name of the AlertmanagerConfig that should be copied over to the + // namespace of the PostgreSQL instance. + AlertmanagerConfigRef string `json:"alertmanagerConfigRef,omitempty"` + + // AlertmanagerConfigSecretRef contains the name of the secret that is used + // in the referenced AlertmanagerConfig + AlertmanagerConfigSecretRef string `json:"alertmanagerConfigSecretRef,omitempty"` + + // AlertmanagerConfigSpecTemplate takes an AlertmanagerConfigSpec object. + // This takes precedence over the AlertmanagerConfigRef. + AlertmanagerConfigSpecTemplate *alertmanagerv1alpha1.AlertmanagerConfigSpec `json:"alertmanagerConfigTemplate,omitempty"` + + // Email necessary to send alerts via email + Email string `json:"email,omitempty"` +} + +// VSHNPostgreSQLEncryption contains storage encryption specific parameters +type VSHNPostgreSQLEncryption struct { + + // Enabled specifies if the instance should use encrypted storage for the instance. + Enabled bool `json:"enabled,omitempty"` +} + +// VSHNPostgreSQLStatus reflects the observed state of a VSHNPostgreSQL. +type VSHNPostgreSQLStatus struct { + // InstanceNamespace contains the name of the namespace where the instance resides + InstanceNamespace string `json:"instanceNamespace,omitempty"` + // PostgreSQLConditions contains the status conditions of the backing object. + PostgreSQLConditions []apisv1.Condition `json:"postgresqlConditions,omitempty"` + NamespaceConditions []apisv1.Condition `json:"namespaceConditions,omitempty"` + ProfileConditions []apisv1.Condition `json:"profileConditions,omitempty"` + PGConfigConditions []apisv1.Condition `json:"pgconfigConditions,omitempty"` + PGClusterConditions []apisv1.Condition `json:"pgclusterConditions,omitempty"` + SecretsConditions []apisv1.Condition `json:"secretConditions,omitempty"` + ObjectBucketConditions []apisv1.Condition `json:"ObjectBucketConditions,omitempty"` + ObjectBackupConfigConditions []apisv1.Condition `json:"ObjectBackupConfigConditions,omitempty"` + NetworkPolicyConditions []apisv1.Condition `json:"networkPolicyConditions,omitempty"` + LocalCAConditions []apisv1.Condition `json:"localCAConditions,omitempty"` + CertificateConditions []apisv1.Condition `json:"certificateConditions,omitempty"` + // IsEOL indicates if this instance is using an EOL version of PostgreSQL. + IsEOL bool `json:"isEOL,omitempty"` +} + +// +kubebuilder:object:root=true + +// VSHNPostgreSQLList defines a list of VSHNPostgreSQL +type VSHNPostgreSQLList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []VSHNPostgreSQL `json:"items"` +} + +// +kubebuilder:object:generate=true +// +kubebuilder:object:root=true + +// XVSHNPostgreSQL represents the internal composite of this claim +type XVSHNPostgreSQL VSHNPostgreSQL + +// +kubebuilder:object:generate=true +// +kubebuilder:object:root=true + +// XVSHNPostgreSQLList represents a list of composites +type XVSHNPostgreSQLList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []XVSHNPostgreSQL `json:"items"` +} diff --git a/apis/vshn/v1/dbaas_vshn_redis.go b/apis/vshn/v1/dbaas_vshn_redis.go new file mode 100644 index 0000000..e9f6029 --- /dev/null +++ b/apis/vshn/v1/dbaas_vshn_redis.go @@ -0,0 +1,130 @@ +package v1 + +import ( + v1 "github.com/vshn/appcat-apiserver/apis/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Workaround to make nested defaulting work. +// kubebuilder is unable to set a {} default +//go:generate yq -i e ../../generated/vshn.appcat.vshn.io_vshnredis.yaml --expression "with(.spec.versions[]; .schema.openAPIV3Schema.properties.spec.properties.parameters.default={})" +//go:generate yq -i e ../../generated/vshn.appcat.vshn.io_vshnredis.yaml --expression "with(.spec.versions[]; .schema.openAPIV3Schema.properties.spec.properties.parameters.properties.size.default={})" +//go:generate yq -i e ../../generated/vshn.appcat.vshn.io_vshnredis.yaml --expression "with(.spec.versions[]; .schema.openAPIV3Schema.properties.spec.properties.parameters.properties.service.default={})" +//go:generate yq -i e ../../generated/vshn.appcat.vshn.io_vshnredis.yaml --expression "with(.spec.versions[]; .schema.openAPIV3Schema.properties.spec.properties.parameters.properties.tls.default={})" +//go:generate yq -i e ../../generated/vshn.appcat.vshn.io_vshnredis.yaml --expression "with(.spec.versions[]; .schema.openAPIV3Schema.properties.spec.properties.parameters.properties.backup.default={})" + +// +kubebuilder:object:root=true + +// VSHNRedis is the API for creating Redis clusters. +type VSHNRedis struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of a VSHNRedis. + Spec VSHNRedisSpec `json:"spec"` + + // Status reflects the observed state of a VSHNRedis. + Status VSHNRedisStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:generate=true +// +kubebuilder:object:root=true +type VSHNRedisList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []VSHNRedis `json:"items,omitempty"` +} + +// VSHNRedisSpec defines the desired state of a VSHNRedis. +type VSHNRedisSpec struct { + // Parameters are the configurable fields of a VSHNRedis. + Parameters VSHNRedisParameters `json:"parameters,omitempty"` + + // WriteConnectionSecretToRef references a secret to which the connection details will be written. + WriteConnectionSecretToRef v1.LocalObjectReference `json:"writeConnectionSecretToRef,omitempty"` +} + +// VSHNRedisParameters are the configurable fields of a VSHNRedis. +type VSHNRedisParameters struct { + // Service contains Redis DBaaS specific properties + Service VSHNRedisServiceSpec `json:"service,omitempty"` + + // Size contains settings to control the sizing of a service. + Size VSHNRedisSizeSpec `json:"size,omitempty"` + + // Scheduling contains settings to control the scheduling of an instance. + Scheduling VSHNDBaaSSchedulingSpec `json:"scheduling,omitempty"` + + // TLS contains settings to control tls traffic of a service. + TLS VSHNRedisTLSSpec `json:"tls,omitempty"` + + // Backup contains settings to control how the instance should get backed up. + Backup K8upBackupSpec `json:"backup,omitempty"` + + // Restore contains settings to control the restore of an instance. + Restore K8upRestoreSpec `json:"restore,omitempty"` + + // Maintenance contains settings to control the maintenance of an instance. + Maintenance VSHNDBaaSMaintenanceScheduleSpec `json:"maintenance,omitempty"` +} + +// VSHNRedisServiceSpec contains Redis DBaaS specific properties +type VSHNRedisServiceSpec struct { + // +kubebuilder:validation:Enum="6.2";"7.0" + // +kubebuilder:default="7.0" + + // Version contains supported version of Redis. + // Multiple versions are supported. The latest version "7.0" is the default version. + Version string `json:"version,omitempty"` + + // RedisSettings contains additional Redis settings. + RedisSettings string `json:"redisSettings,omitempty"` +} + +// VSHNRedisSizeSpec contains settings to control the sizing of a service. +type VSHNRedisSizeSpec struct { + + // CPURequests defines the requests amount of Kubernetes CPUs for an instance. + CPURequests string `json:"cpuRequests,omitempty"` + + // CPULimits defines the limits amount of Kubernetes CPUs for an instance. + CPULimits string `json:"cpuLimits,omitempty"` + + // MemoryRequests defines the requests amount of memory in units of bytes for an instance. + MemoryRequests string `json:"memoryRequests,omitempty"` + + // MemoryLimits defines the limits amount of memory in units of bytes for an instance. + MemoryLimits string `json:"memoryLimits,omitempty"` + + // Disk defines the amount of disk space for an instance. + Disk string `json:"disk,omitempty"` + + // Plan is the name of the resource plan that defines the compute resources. + Plan string `json:"plan,omitempty"` +} + +// VSHNRedisTLSSpec contains settings to control tls traffic of a service. +type VSHNRedisTLSSpec struct { + // +kubebuilder:default=true + + // TLSEnabled enables TLS traffic for the service + TLSEnabled bool `json:"enabled,omitempty"` + + // +kubebuilder:default=true + // TLSAuthClients enables client authentication requirement + TLSAuthClients bool `json:"authClients,omitempty"` +} + +// VSHNRedisStatus reflects the observed state of a VSHNRedis. +type VSHNRedisStatus struct { + // RedisConditions contains the status conditions of the backing object. + NamespaceConditions []v1.Condition `json:"namespaceConditions,omitempty"` + SelfSignedIssuerConditions []v1.Condition `json:"selfSignedIssuerConditions,omitempty"` + LocalCAConditions []v1.Condition `json:"localCAConditions,omitempty"` + CaCertificateConditions []v1.Condition `json:"caCertificateConditions,omitempty"` + ServerCertificateConditions []v1.Condition `json:"serverCertificateConditions,omitempty"` + ClientCertificateConditions []v1.Condition `json:"clientCertificateConditions,omitempty"` + // InstanceNamespace contains the name of the namespace where the instance resides + InstanceNamespace string `json:"instanceNamespace,omitempty"` +} diff --git a/apis/vshn/v1/groupversion_info.go b/apis/vshn/v1/groupversion_info.go new file mode 100644 index 0000000..a574426 --- /dev/null +++ b/apis/vshn/v1/groupversion_info.go @@ -0,0 +1,32 @@ +// +kubebuilder:object:generate=true +// +groupName=vshn.appcat.vshn.io +// +versionName=v1 + +package v1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "vshn.appcat.vshn.io", Version: "v1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) + +func init() { + SchemeBuilder.Register( + &VSHNPostgreSQL{}, + &VSHNPostgreSQLList{}, + &XVSHNPostgreSQL{}, + &XVSHNPostgreSQLList{}, + &VSHNRedis{}, + &VSHNRedisList{}, + ) +} diff --git a/apis/vshn/v1/zz_generated.deepcopy.go b/apis/vshn/v1/zz_generated.deepcopy.go new file mode 100644 index 0000000..d1e51f0 --- /dev/null +++ b/apis/vshn/v1/zz_generated.deepcopy.go @@ -0,0 +1,731 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v1 + +import ( + "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1" + apisv1 "github.com/vshn/appcat-apiserver/apis/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *K8upBackupSpec) DeepCopyInto(out *K8upBackupSpec) { + *out = *in + out.Retention = in.Retention +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8upBackupSpec. +func (in *K8upBackupSpec) DeepCopy() *K8upBackupSpec { + if in == nil { + return nil + } + out := new(K8upBackupSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *K8upRestoreSpec) DeepCopyInto(out *K8upRestoreSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8upRestoreSpec. +func (in *K8upRestoreSpec) DeepCopy() *K8upRestoreSpec { + if in == nil { + return nil + } + out := new(K8upRestoreSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *K8upRetentionPolicy) DeepCopyInto(out *K8upRetentionPolicy) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8upRetentionPolicy. +func (in *K8upRetentionPolicy) DeepCopy() *K8upRetentionPolicy { + if in == nil { + return nil + } + out := new(K8upRetentionPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNDBaaSMaintenanceScheduleSpec) DeepCopyInto(out *VSHNDBaaSMaintenanceScheduleSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNDBaaSMaintenanceScheduleSpec. +func (in *VSHNDBaaSMaintenanceScheduleSpec) DeepCopy() *VSHNDBaaSMaintenanceScheduleSpec { + if in == nil { + return nil + } + out := new(VSHNDBaaSMaintenanceScheduleSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNDBaaSNetworkSpec) DeepCopyInto(out *VSHNDBaaSNetworkSpec) { + *out = *in + if in.IPFilter != nil { + in, out := &in.IPFilter, &out.IPFilter + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNDBaaSNetworkSpec. +func (in *VSHNDBaaSNetworkSpec) DeepCopy() *VSHNDBaaSNetworkSpec { + if in == nil { + return nil + } + out := new(VSHNDBaaSNetworkSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNDBaaSPostgresExtension) DeepCopyInto(out *VSHNDBaaSPostgresExtension) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNDBaaSPostgresExtension. +func (in *VSHNDBaaSPostgresExtension) DeepCopy() *VSHNDBaaSPostgresExtension { + if in == nil { + return nil + } + out := new(VSHNDBaaSPostgresExtension) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNDBaaSSchedulingSpec) DeepCopyInto(out *VSHNDBaaSSchedulingSpec) { + *out = *in + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNDBaaSSchedulingSpec. +func (in *VSHNDBaaSSchedulingSpec) DeepCopy() *VSHNDBaaSSchedulingSpec { + if in == nil { + return nil + } + out := new(VSHNDBaaSSchedulingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNDBaaSSizeRequestsSpec) DeepCopyInto(out *VSHNDBaaSSizeRequestsSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNDBaaSSizeRequestsSpec. +func (in *VSHNDBaaSSizeRequestsSpec) DeepCopy() *VSHNDBaaSSizeRequestsSpec { + if in == nil { + return nil + } + out := new(VSHNDBaaSSizeRequestsSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNDBaaSSizeSpec) DeepCopyInto(out *VSHNDBaaSSizeSpec) { + *out = *in + out.Requests = in.Requests +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNDBaaSSizeSpec. +func (in *VSHNDBaaSSizeSpec) DeepCopy() *VSHNDBaaSSizeSpec { + if in == nil { + return nil + } + out := new(VSHNDBaaSSizeSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgreSQL) DeepCopyInto(out *VSHNPostgreSQL) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgreSQL. +func (in *VSHNPostgreSQL) DeepCopy() *VSHNPostgreSQL { + if in == nil { + return nil + } + out := new(VSHNPostgreSQL) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VSHNPostgreSQL) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgreSQLBackup) DeepCopyInto(out *VSHNPostgreSQLBackup) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgreSQLBackup. +func (in *VSHNPostgreSQLBackup) DeepCopy() *VSHNPostgreSQLBackup { + if in == nil { + return nil + } + out := new(VSHNPostgreSQLBackup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgreSQLEncryption) DeepCopyInto(out *VSHNPostgreSQLEncryption) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgreSQLEncryption. +func (in *VSHNPostgreSQLEncryption) DeepCopy() *VSHNPostgreSQLEncryption { + if in == nil { + return nil + } + out := new(VSHNPostgreSQLEncryption) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgreSQLList) DeepCopyInto(out *VSHNPostgreSQLList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VSHNPostgreSQL, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgreSQLList. +func (in *VSHNPostgreSQLList) DeepCopy() *VSHNPostgreSQLList { + if in == nil { + return nil + } + out := new(VSHNPostgreSQLList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VSHNPostgreSQLList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgreSQLMonitoring) DeepCopyInto(out *VSHNPostgreSQLMonitoring) { + *out = *in + if in.AlertmanagerConfigSpecTemplate != nil { + in, out := &in.AlertmanagerConfigSpecTemplate, &out.AlertmanagerConfigSpecTemplate + *out = new(v1alpha1.AlertmanagerConfigSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgreSQLMonitoring. +func (in *VSHNPostgreSQLMonitoring) DeepCopy() *VSHNPostgreSQLMonitoring { + if in == nil { + return nil + } + out := new(VSHNPostgreSQLMonitoring) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgreSQLParameters) DeepCopyInto(out *VSHNPostgreSQLParameters) { + *out = *in + in.Service.DeepCopyInto(&out.Service) + out.Maintenance = in.Maintenance + out.Size = in.Size + in.Scheduling.DeepCopyInto(&out.Scheduling) + in.Network.DeepCopyInto(&out.Network) + out.Backup = in.Backup + out.Restore = in.Restore + in.Monitoring.DeepCopyInto(&out.Monitoring) + out.Encryption = in.Encryption + out.UpdateStrategy = in.UpdateStrategy + out.Replication = in.Replication +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgreSQLParameters. +func (in *VSHNPostgreSQLParameters) DeepCopy() *VSHNPostgreSQLParameters { + if in == nil { + return nil + } + out := new(VSHNPostgreSQLParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgreSQLReplicationStrategy) DeepCopyInto(out *VSHNPostgreSQLReplicationStrategy) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgreSQLReplicationStrategy. +func (in *VSHNPostgreSQLReplicationStrategy) DeepCopy() *VSHNPostgreSQLReplicationStrategy { + if in == nil { + return nil + } + out := new(VSHNPostgreSQLReplicationStrategy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgreSQLRestore) DeepCopyInto(out *VSHNPostgreSQLRestore) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgreSQLRestore. +func (in *VSHNPostgreSQLRestore) DeepCopy() *VSHNPostgreSQLRestore { + if in == nil { + return nil + } + out := new(VSHNPostgreSQLRestore) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgreSQLServiceSpec) DeepCopyInto(out *VSHNPostgreSQLServiceSpec) { + *out = *in + in.PostgreSQLSettings.DeepCopyInto(&out.PostgreSQLSettings) + if in.Extensions != nil { + in, out := &in.Extensions, &out.Extensions + *out = make([]VSHNDBaaSPostgresExtension, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgreSQLServiceSpec. +func (in *VSHNPostgreSQLServiceSpec) DeepCopy() *VSHNPostgreSQLServiceSpec { + if in == nil { + return nil + } + out := new(VSHNPostgreSQLServiceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgreSQLSpec) DeepCopyInto(out *VSHNPostgreSQLSpec) { + *out = *in + in.Parameters.DeepCopyInto(&out.Parameters) + out.WriteConnectionSecretToRef = in.WriteConnectionSecretToRef + out.ResourceRef = in.ResourceRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgreSQLSpec. +func (in *VSHNPostgreSQLSpec) DeepCopy() *VSHNPostgreSQLSpec { + if in == nil { + return nil + } + out := new(VSHNPostgreSQLSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgreSQLStatus) DeepCopyInto(out *VSHNPostgreSQLStatus) { + *out = *in + if in.PostgreSQLConditions != nil { + in, out := &in.PostgreSQLConditions, &out.PostgreSQLConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NamespaceConditions != nil { + in, out := &in.NamespaceConditions, &out.NamespaceConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ProfileConditions != nil { + in, out := &in.ProfileConditions, &out.ProfileConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.PGConfigConditions != nil { + in, out := &in.PGConfigConditions, &out.PGConfigConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.PGClusterConditions != nil { + in, out := &in.PGClusterConditions, &out.PGClusterConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SecretsConditions != nil { + in, out := &in.SecretsConditions, &out.SecretsConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ObjectBucketConditions != nil { + in, out := &in.ObjectBucketConditions, &out.ObjectBucketConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ObjectBackupConfigConditions != nil { + in, out := &in.ObjectBackupConfigConditions, &out.ObjectBackupConfigConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NetworkPolicyConditions != nil { + in, out := &in.NetworkPolicyConditions, &out.NetworkPolicyConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.LocalCAConditions != nil { + in, out := &in.LocalCAConditions, &out.LocalCAConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.CertificateConditions != nil { + in, out := &in.CertificateConditions, &out.CertificateConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgreSQLStatus. +func (in *VSHNPostgreSQLStatus) DeepCopy() *VSHNPostgreSQLStatus { + if in == nil { + return nil + } + out := new(VSHNPostgreSQLStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNPostgreSQLUpdateStrategy) DeepCopyInto(out *VSHNPostgreSQLUpdateStrategy) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNPostgreSQLUpdateStrategy. +func (in *VSHNPostgreSQLUpdateStrategy) DeepCopy() *VSHNPostgreSQLUpdateStrategy { + if in == nil { + return nil + } + out := new(VSHNPostgreSQLUpdateStrategy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedis) DeepCopyInto(out *VSHNRedis) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedis. +func (in *VSHNRedis) DeepCopy() *VSHNRedis { + if in == nil { + return nil + } + out := new(VSHNRedis) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VSHNRedis) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisList) DeepCopyInto(out *VSHNRedisList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VSHNRedis, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisList. +func (in *VSHNRedisList) DeepCopy() *VSHNRedisList { + if in == nil { + return nil + } + out := new(VSHNRedisList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VSHNRedisList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisParameters) DeepCopyInto(out *VSHNRedisParameters) { + *out = *in + out.Service = in.Service + out.Size = in.Size + in.Scheduling.DeepCopyInto(&out.Scheduling) + out.TLS = in.TLS + out.Backup = in.Backup + out.Restore = in.Restore + out.Maintenance = in.Maintenance +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisParameters. +func (in *VSHNRedisParameters) DeepCopy() *VSHNRedisParameters { + if in == nil { + return nil + } + out := new(VSHNRedisParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisServiceSpec) DeepCopyInto(out *VSHNRedisServiceSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisServiceSpec. +func (in *VSHNRedisServiceSpec) DeepCopy() *VSHNRedisServiceSpec { + if in == nil { + return nil + } + out := new(VSHNRedisServiceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisSizeSpec) DeepCopyInto(out *VSHNRedisSizeSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisSizeSpec. +func (in *VSHNRedisSizeSpec) DeepCopy() *VSHNRedisSizeSpec { + if in == nil { + return nil + } + out := new(VSHNRedisSizeSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisSpec) DeepCopyInto(out *VSHNRedisSpec) { + *out = *in + in.Parameters.DeepCopyInto(&out.Parameters) + out.WriteConnectionSecretToRef = in.WriteConnectionSecretToRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisSpec. +func (in *VSHNRedisSpec) DeepCopy() *VSHNRedisSpec { + if in == nil { + return nil + } + out := new(VSHNRedisSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisStatus) DeepCopyInto(out *VSHNRedisStatus) { + *out = *in + if in.NamespaceConditions != nil { + in, out := &in.NamespaceConditions, &out.NamespaceConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SelfSignedIssuerConditions != nil { + in, out := &in.SelfSignedIssuerConditions, &out.SelfSignedIssuerConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.LocalCAConditions != nil { + in, out := &in.LocalCAConditions, &out.LocalCAConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.CaCertificateConditions != nil { + in, out := &in.CaCertificateConditions, &out.CaCertificateConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ServerCertificateConditions != nil { + in, out := &in.ServerCertificateConditions, &out.ServerCertificateConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ClientCertificateConditions != nil { + in, out := &in.ClientCertificateConditions, &out.ClientCertificateConditions + *out = make([]apisv1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisStatus. +func (in *VSHNRedisStatus) DeepCopy() *VSHNRedisStatus { + if in == nil { + return nil + } + out := new(VSHNRedisStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSHNRedisTLSSpec) DeepCopyInto(out *VSHNRedisTLSSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSHNRedisTLSSpec. +func (in *VSHNRedisTLSSpec) DeepCopy() *VSHNRedisTLSSpec { + if in == nil { + return nil + } + out := new(VSHNRedisTLSSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *XVSHNPostgreSQL) DeepCopyInto(out *XVSHNPostgreSQL) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new XVSHNPostgreSQL. +func (in *XVSHNPostgreSQL) DeepCopy() *XVSHNPostgreSQL { + if in == nil { + return nil + } + out := new(XVSHNPostgreSQL) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *XVSHNPostgreSQL) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *XVSHNPostgreSQLList) DeepCopyInto(out *XVSHNPostgreSQLList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]XVSHNPostgreSQL, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new XVSHNPostgreSQLList. +func (in *XVSHNPostgreSQLList) DeepCopy() *XVSHNPostgreSQLList { + if in == nil { + return nil + } + out := new(XVSHNPostgreSQLList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *XVSHNPostgreSQLList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/cmd/apiserver.go b/cmd/apiserver.go new file mode 100644 index 0000000..c93aabd --- /dev/null +++ b/cmd/apiserver.go @@ -0,0 +1,65 @@ +package cmd + +import ( + "log" + "os" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + appcatv1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + "github.com/vshn/appcat-apiserver/pkg/apiserver/appcat" + vshnpostgres "github.com/vshn/appcat-apiserver/pkg/apiserver/vshn/postgres" + vshnredis "github.com/vshn/appcat-apiserver/pkg/apiserver/vshn/redis" + "sigs.k8s.io/apiserver-runtime/pkg/builder" +) + +var apiServerCMDStr = "apiserver" + +var APIServerCMD = newAPIServerCMD() + +func newAPIServerCMD() *cobra.Command { + + viper.AutomaticEnv() + + var appcatEnabled, vshnPGBackupsEnabled, vshnRedisBackupsEnabled bool + + if len(os.Args) < 2 { + return &cobra.Command{} + } + + if os.Args[1] == apiServerCMDStr { + appcatEnabled = viper.GetBool("APPCAT_HANDLER_ENABLED") + vshnPGBackupsEnabled = viper.GetBool("VSHN_POSTGRES_BACKUP_HANDLER_ENABLED") + vshnRedisBackupsEnabled = viper.GetBool("VSHN_REDIS_BACKUP_HANDLER_ENABLED") + if !appcatEnabled && !vshnPGBackupsEnabled && !vshnRedisBackupsEnabled { + log.Fatal("Handlers are not enabled, please set at least one of APPCAT_HANDLER_ENABLED | VSHN_POSTGRES_BACKUP_HANDLER_ENABLED | VSHN_REDIS_BACKUP_HANDLER_ENABLED env variables to True") + } + } + + b := builder.APIServer + + if appcatEnabled { + b.WithResourceAndHandler(&appcatv1.AppCat{}, appcat.New()) + } + + if vshnPGBackupsEnabled { + b.WithResourceAndHandler(&appcatv1.VSHNPostgresBackup{}, vshnpostgres.New()) + } + + if vshnRedisBackupsEnabled { + b.WithResourceAndHandler(&appcatv1.VSHNRedisBackup{}, vshnredis.New()) + } + + b.WithoutEtcd(). + ExposeLoopbackAuthorizer(). + ExposeLoopbackMasterClientConfig() + + cmd, err := b.Build() + cmd.Use = "apiserver" + cmd.Short = "AppCat API Server" + cmd.Long = "Run the AppCat API Server" + if err != nil { + log.Fatal(err, "Unable to load build API Server") + } + return cmd +} diff --git a/config/apiserver/aggregated-apiserver.yaml b/config/apiserver/aggregated-apiserver.yaml new file mode 100644 index 0000000..d3f7ad3 --- /dev/null +++ b/config/apiserver/aggregated-apiserver.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: appcat-apiserver + namespace: appcat + labels: + api: appcat + apiserver: "true" +spec: + selector: + matchLabels: + api: appcat + apiserver: "true" + replicas: 1 + template: + metadata: + labels: + api: appcat + apiserver: "true" + spec: + containers: + - name: apiserver + image: "ghcr.io/vshn/appcat:v0.0.1" #patched + volumeMounts: + - name: apiserver-certs + mountPath: /apiserver.local.config/certificates + readOnly: true + args: + - "apiserver" + - "--secure-port=9443" + - "--tls-cert-file=/apiserver.local.config/certificates/tls.crt" + - "--tls-private-key-file=/apiserver.local.config/certificates/tls.key" + - "--audit-log-path=-" + - "--feature-gates=APIPriorityAndFairness=false" + - "--audit-log-maxage=0" + - "--audit-log-maxbackup=0" + envFrom: + - configMapRef: + name: apiserver-envs + resources: + requests: + cpu: 100m + memory: 50Mi + limits: + cpu: 200m + memory: 200Mi + serviceAccountName: appcat-apiserver + volumes: + - name: apiserver-certs + secret: + secretName: appcat diff --git a/config/apiserver/apiserver-cert.yaml b/config/apiserver/apiserver-cert.yaml new file mode 100644 index 0000000..9e0eb36 --- /dev/null +++ b/config/apiserver/apiserver-cert.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Secret +type: kubernetes.io/tls +metadata: + name: appcat + namespace: appcat + labels: + api: appcat + apiserver: "true" +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURpVENDQW5HZ0F3SUJBZ0lJZXh2Rkd0dGkzRjR3RFFZSktvWklodmNOQVFFTEJRQXdaakVMTUFrR0ExVUUKQmhNQ2RXNHhDekFKQmdOVkJBZ01Bbk4wTVFvd0NBWURWUVFIREFGc01Rb3dDQVlEVlFRS0RBRnZNUXN3Q1FZRApWUVFMREFKdmRURWxNQ01HQTFVRUF3d2NZWEJ3WTJGMExXTmxjblJwWm1sallYUmxMV0YxZEdodmNtbDBlVEFlCkZ3MHlNekF4TWpReE1EQXhOVFZhRncwek16QXhNakl3T0RVNU5EaGFNQjB4R3pBWkJnTlZCQU1URW1Gd2NHTmgKZEM1a1pXWmhkV3gwTG5OMll6Q0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUx6VQpiQWR4d3J3YkdkTzVCSDA1dUtuNnYrQjd0ODFFYUhUUUtEY2srZFFockpxNTM0U2x2UElZV25UYTM0eVM2eWZjCjk4aFlLcml2ZVhjYmlxcFVFZVNMWWxpOEJhNUhPMXNVdUU3VEpURFFIM2Z6amtOM2p1Uy9Nd2ZFQU9vU0VJQjUKR2tNMG1LblJaaTJaQ3V4dEtqc25CT0VJRmJobWlMQUxCVDNFdi9KaERma2cxY0s0dWpPTkJWQ0ZGOWxHdzFUdApoa2xWOFdURFIwb1RYeUt0cXRudm5LOFNQZmFET2RVUW1DdDRLOEhsZWtqdDkraThUOFRzN0o5cXRDQXdJNXphCk9xa2ZKUXNOQnRRdDMvY1FFWVdyb0lVWDBYbzg3MzV2enJoK0QwaXh4NjJpdzJWWStBTUdZbkJFdGQ4dFlnKzEKaFhDOUdQYkdFaHdqNzJMdkgyY0NBd0VBQWFPQmd6Q0JnREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdIUVlEVlIwbApCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01COEdBMVVkSXdRWU1CYUFGRGxSWDJWQ3hDUVoxTmFPCjFjMWlHYmk3VWZ1M01DNEdBMVVkRVFRbk1DV0NDV3h2WTJGc2FHOXpkSUlTWVhCd1kyRjBMbVJsWm1GMWJIUXUKYzNaamh3Ui9BQUFCTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFDaXJrQmVuSlQxQXMwVnNFd0RVbWZNcyt1MQpKUUJycDZIVU9LNXVGMHY4WlhqNzdCM3pub1ZTMG1lSDhzR1B0L1BkZjBCbkJYTWZEMEVWQjZ4UjNZV3Q4RHZSCmdEbG9Wa0tsYlp4dHJpMG45K2lsRHZjRWJObHlRK2lDbGFlVWtEVWUzSDlLa1ZkNkdMLzJtSWtMWWh4ajcvZGsKaFNieS9oVHd4Rkd1aVBNKzZtZ25jc3AzdWNZT0tBdTFyRmt4aHpQTGpEYzlyRUlIVFhHNjNvNVFVMFFPWEZLRApJeVFwR0RQeUpTOVhnMEZ0R0Y0eHNWTk9uTEZuazlHTmJxUlZvUEFxVll6bXF1ZnVaS250S1I3KzBweGk3K3orCkN2QjVHZ3lxKzdmWVhadzgwbTZQQWtaUkZ3QzdCMGljRytVZFdhK0ZaazMwT1o0R0pzdlM4aEdqRjl2bwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdk5Sc0IzSEN2QnNaMDdrRWZUbTRxZnEvNEh1M3pVUm9kTkFvTnlUNTFDR3Ntcm5mCmhLVzg4aGhhZE5yZmpKTHJKOXozeUZncXVLOTVkeHVLcWxRUjVJdGlXTHdGcmtjN1d4UzRUdE1sTU5BZmQvT08KUTNlTzVMOHpCOFFBNmhJUWdIa2FRelNZcWRGbUxaa0s3RzBxT3ljRTRRZ1Z1R2FJc0FzRlBjUy84bUVOK1NEVgp3cmk2TTQwRlVJVVgyVWJEVk8yR1NWWHhaTU5IU2hOZklxMnEyZStjcnhJOTlvTTUxUkNZSzNncndlVjZTTzMzCjZMeFB4T3pzbjJxMElEQWpuTm82cVI4bEN3MEcxQzNmOXhBUmhhdWdoUmZSZWp6dmZtL091SDRQU0xISHJhTEQKWlZqNEF3WmljRVMxM3kxaUQ3V0ZjTDBZOXNZU0hDUHZZdThmWndJREFRQUJBb0lCQUMzUExzSURsQ1dFUVppKwppdHRDVkkxUUdwcUFDUkFRSjNNblNJcFFPeGQrYjl3OWVYODVvZ1B0YW94c3lNeldtNXZaSEhlTEJCbzMzN1RJCmhyUlpudG1lQXViWi9sclFSeE1Lak1mNnEwd1RRWDhkSkFjMEN3TnRheGpZTVF5WUEwN1hra3A1aVF1eGxDTTkKdkl1czFCRHpuMWhrV0xpYWoxQjk2L0NYT2FXNEFPZ1VWTFY0WTI4cDFPd0Vza0FzYzFmQWdhUzMvQ202aHk2bQpvaXB3bG1SM28xL01PR0V4Z1BWVHl2cU5jZGZpUDRnR0phekdHM3ZWamg5WldraUd6d0JrSjlQSGlhMytraFhkCmhrcE5CYkRMN1A3cW9TVVA2SWV1ZjRIOUZYZGFVS0s5bEJDSkdVclZIbFJKYXgxZjB2VldacU9rVk55am1zcXAKNUdIa25Ea0NnWUVBeTQ1dEIxblhFQXhKejFtcXJGLzdTY3YzREV5Qzh1dlBXeVQ5V1JYeWFoVkpISXBheksvNQpQeU9Rd052UmpHbHY2QmVGZTUzUDN2YUNmbFZtYlRKWjd1Uk5IT3ErZk1rU0hBbUNGRmROMzJRdHF3eUN6V0hUCkZZVHNxbTVTbkp5N0V2T0t4NUFVRTMxTDBiQ1pVZVhoZFFocjBLNjArbnljNENHZU05em5HT1VDZ1lFQTdYcTAKSFpuTDloQlFQRk9yVVhTbGNVRm8xeEZMblFPTGJKblRiaUVySFNKa3hyVzhCMUh3MmFmUGFSTGdYMmFRRlhMKwp6SmZEbm14WEM0NHU4OHhZQm1TczNYZUtZcVJLOE9jOTNmTldVS0ZZWVlnY01EUGw2OUptbzRoOVdZT1JkWUxRCk5LdUg4Vk1Fb1dBTHdKd2lLMEk2OFBZV3V6SG54RzlRRElTdHpsc0NnWUVBeHpoc3NzNFZzd29qendEMkRtQ2QKNHRyeHZpSy80cG94eGdXTXd2eTgxV0JodnNJV0hkQjRnM2YrZXhKb295Q3FPcCtTcjZxRFFMZElmZlo5R1pBMgovcHlJY0MyN0l3dnpZbWRYM1NxWTkzTG5VMG9hVm9KUWdmWW5YcUk4empTQ3p3aDJvZHY4R2hyOThvc0JjMnNtClhsRlFtOXZ4R2xxTmVyck1SL3dDWFpVQ2dZRUFoa0xsS2djVnBSdzhBSjlkTUVRK2RQV0NESFdleGpxR3pQTjYKOGJ5VzMzWGVlK21yVUlnbkh5Y2N3RThIdzJmODllTjU0cm02d3dxRmUrYjY3S01POFM1aC9TUGtmVGhkbGkxbQpjT0Z1WDArWTVDdExwSVR5N0l5YnNzRXIvVHZCTlNxc0E5c21sRzFYSk5Wa1Q1VDJUSGE1VkJvTlovQWpGSS9LCk5nbWRLaGtDZ1lBb1A4dW04azZ0TFZxM0lMU1JINm9oRm40a3d6cFpGTDd5K1h6S2xkMTVtUGdsc3BTQnFnTEIKelhrMFBQc2QwV3NvTVVieWhocjB5ZzQxUnlJQnA1dUhWR1F5MSt5MFdPaWZaM2dveHNDTllnVkJEdU45QjUzTQpzWFJrWCtwaGFwK0dUQkx2VDVsRnFWRTYvME5JZmE2eFB4V3NMUjJCWmwyMzlVVnMvWFlrWVE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo= diff --git a/config/apiserver/apiserver-envs.yaml b/config/apiserver/apiserver-envs.yaml new file mode 100644 index 0000000..13641d6 --- /dev/null +++ b/config/apiserver/apiserver-envs.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: apiserver-envs + namespace: appcat + labels: + api: appcat + apiserver: "true" +data: + APPCAT_HANDLER_ENABLED: "true" + VSHN_POSTGRES_BACKUP_HANDLER_ENABLED: "true" diff --git a/config/apiserver/apiservice.yaml b/config/apiserver/apiservice.yaml new file mode 100644 index 0000000..6190965 --- /dev/null +++ b/config/apiserver/apiservice.yaml @@ -0,0 +1,16 @@ +apiVersion: apiregistration.k8s.io/v1 +kind: APIService +metadata: + name: v1.api.appcat.vshn.io + labels: + api: appcat + apiserver: "true" +spec: + version: v1 + group: api.appcat.vshn.io + groupPriorityMinimum: 2000 + service: + name: appcat + namespace: appcat + port: 443 + versionPriority: 10 diff --git a/config/apiserver/cluster-role-binding.yaml b/config/apiserver/cluster-role-binding.yaml new file mode 100644 index 0000000..9fff5b0 --- /dev/null +++ b/config/apiserver/cluster-role-binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: appcat-apiserver +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: appcat-apiserver +subjects: + - kind: ServiceAccount + name: appcat-apiserver + namespace: appcat diff --git a/config/apiserver/namespace.yaml b/config/apiserver/namespace.yaml new file mode 100644 index 0000000..947da61 --- /dev/null +++ b/config/apiserver/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: appcat diff --git a/config/apiserver/role.yaml b/config/apiserver/role.yaml new file mode 100644 index 0000000..840e88f --- /dev/null +++ b/config/apiserver/role.yaml @@ -0,0 +1,81 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: appcat +rules: +- apiGroups: + - "" + resourceNames: + - extension-apiserver-authentication + resources: + - configmaps + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - namespaces + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + - validatingwebhookconfigurations + verbs: + - get + - list + - watch +- apiGroups: + - apiextensions.crossplane.io + resources: + - compositions + verbs: + - get + - list + - watch +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + - delete + - get + - list + - update + - watch +- apiGroups: + - k8up.io + resources: + - snapshots + verbs: + - get + - list + - watch +- apiGroups: + - stackgres.io + resources: + - sgbackups + verbs: + - get + - list + - watch +- apiGroups: + - vshn.appcat.vshn.io + resources: + - vshnredis + - xvshnpostgresqls + verbs: + - get + - list + - watch diff --git a/config/apiserver/service-account.yaml b/config/apiserver/service-account.yaml new file mode 100644 index 0000000..ad7cc8d --- /dev/null +++ b/config/apiserver/service-account.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: appcat-apiserver + namespace: appcat diff --git a/config/apiserver/service.yaml b/config/apiserver/service.yaml new file mode 100644 index 0000000..2374c9f --- /dev/null +++ b/config/apiserver/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: appcat + namespace: appcat + labels: + api: appcat + apiserver: "true" +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + api: appcat + apiserver: "true" diff --git a/docs/antora-build.mk b/docs/antora-build.mk new file mode 100644 index 0000000..8341f3c --- /dev/null +++ b/docs/antora-build.mk @@ -0,0 +1,24 @@ +docs_out_dir := ./.public + +docker_opts ?= --rm --tty --user "$$(id -u)" + +antora_build_version ?= latest +antora_cmd ?= $(DOCKER_CMD) run $(docker_opts) --volume "$${PWD}":/antora ghcr.io/vshn/antora:$(antora_build_version) +antora_opts ?= --cache-dir=.cache/antora + +.PHONY: docs +docs: docs-html ## All-in-one docs build + +.PHONY: docs-html +docs-html: $(docs_out_dir)/index.html ## Generate HTML version of documentation with Antora, output at ./.public + @touch $(docs_out_dir)/.nojekyll + +$(docs_out_dir)/index.html: + $(antora_cmd) $(antora_opts) docs/antora-playbook.yml + +.PHONY: docs-publish +docs-publish: docs/node_modules docs-html ## Publishes the Antora documentation on Github Pages + npm --prefix ./docs run deploy + +docs/node_modules: + npm --prefix ./docs install diff --git a/docs/antora-playbook.yml b/docs/antora-playbook.yml new file mode 100644 index 0000000..20b96d6 --- /dev/null +++ b/docs/antora-playbook.yml @@ -0,0 +1,18 @@ +site: + title: Application Catalog + start_page: appcat-apiserver::index.adoc + url: https://vshn.github.io/appcat-apiserver +content: + sources: + - url: ../antora + branches: + - HEAD + - docs/v* + start_path: docs +ui: + bundle: + url: https://github.com/vshn/antora-ui-default/releases/download/2.0.15/ui-bundle.zip + snapshot: true +output: + dir: .public/ + clean: true diff --git a/docs/antora-preview.mk b/docs/antora-preview.mk new file mode 100644 index 0000000..f5fc94c --- /dev/null +++ b/docs/antora-preview.mk @@ -0,0 +1,7 @@ + +antora_preview_version ?= 3.1.4 +antora_preview_cmd ?= $(DOCKER_CMD) run --rm --publish 35729:35729 --publish 2020:2020 --volume "${PWD}":/preview/antora ghcr.io/vshn/antora-preview:$(antora_preview_version) --style=vshn + +.PHONY: docs-preview +docs-preview: ## Preview the documentation + $(antora_preview_cmd) diff --git a/docs/antora.yml b/docs/antora.yml new file mode 100644 index 0000000..7aa95d4 --- /dev/null +++ b/docs/antora.yml @@ -0,0 +1,6 @@ +name: appcat +title: Application Catalog +version: master +start_page: ROOT:index.adoc +nav: + - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/assets/images/.gitkeep b/docs/modules/ROOT/assets/images/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/modules/ROOT/assets/images/quotas.excalidraw.png b/docs/modules/ROOT/assets/images/quotas.excalidraw.png new file mode 100644 index 0000000..b819539 Binary files /dev/null and b/docs/modules/ROOT/assets/images/quotas.excalidraw.png differ diff --git a/docs/modules/ROOT/examples/.gitkeep b/docs/modules/ROOT/examples/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc new file mode 100644 index 0000000..05a90e0 --- /dev/null +++ b/docs/modules/ROOT/nav.adoc @@ -0,0 +1,21 @@ +* xref:index.adoc[Introduction] +* https://github.com/vshn/appcat/releases[Changelog,window=_blank] 🔗 + +.Tutorials +//* xref:tutorials/example.adoc[Dev] + +.How To +* xref:examples/vscode.adoc[] + +.Technical reference +* xref:references/apiserver/env-variables.adoc[Env variables] + +.Explanation +* xref:explanations/apiserver/boostrap.adoc[Boostrap] +* xref:explanations/comp-functions/runtime.adoc[Runtime Library] +* xref:explanations/comp-functions/vshn-postgres.adoc[VSHN Postgres Functions] +* xref:explanations/slareports.adoc[] +* xref:explanations/dev-notes.adoc[] +* xref:explanations/comp-functions/debug.adoc[] +* xref:explanations/comp-functions/redis-pvc-resize.adoc[] +* xref:explanations/webhooks.adoc[] diff --git a/docs/modules/ROOT/pages/examples/.gitkeep b/docs/modules/ROOT/pages/examples/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/modules/ROOT/pages/examples/vscode.adoc b/docs/modules/ROOT/pages/examples/vscode.adoc new file mode 100644 index 0000000..0493f3d --- /dev/null +++ b/docs/modules/ROOT/pages/examples/vscode.adoc @@ -0,0 +1,115 @@ += VSCode Launch Configs + +Here is an example for vscode launch configs for this repository. +Please adjust any parameters and env vars to your use-case, or they may not work! + +[source,json] +---- +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Maintenance", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}", + "args": [ + "maintenance", + "--service", + "postgresql", + "--log-level", + "1", + ], + "env": { + "INSTANCE_NAMESPACE": "vshn-postgresql-pgsql-app1-prod-j8rlk", + "KUBECONFIG": "PATH_TO_YOUR_KIND_CONFIG", + "API_USERNAME": "admin", + "API_PASSWORD": "password", + "CLAIM_NAMESPACE": "default", + "CLAIM_NAME": "pgsql-app1-prod", + "SG_NAMESPACE": "stackgres", + } + }, + { + "name": "Launch sliprober", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}", + "args": [ + "sliprober", + "--vshn-postgresql", + "--log-level", + "1", + ], + "env": { + "KUBECONFIG": "PATH_TO_YOUR_KIND_CONFIG", + } + }, + { + "name": "Launch slareport", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}", + "args": [ + "slareport", + "--previousmonth", + "--mimirorg", + "vshn", + "--log-level", + "1", + ], + "env": { + "KUBECONFIG": "PATH_TO_YOUR_KIND_CONFIG", + } + }, + { + "name": "Launch apiserver", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}", + "args": [ + "apiserver", + "--secure-port=9443", + "--kubeconfig=PATH_TO_YOUR_KIND_CONFIG", + "--authorization-kubeconfig=PATH_TO_YOUR_KIND_CONFIG", + "--authentication-kubeconfig=PATH_TO_YOUR_KIND_CONFIG", + "--feature-gates=APIPriorityAndFairness=false", + ], + "env": { + "KUBECONFIG": "PATH_TO_YOUR_KIND_CONFIG", + "VSHN_REDIS_BACKUP_HANDLER_ENABLED": "true", + "APPCAT_HANDLER_ENABLED": "true", + "VSHN_POSTGRES_BACKUP_HANDLER_ENABLED": "true" + } + }, + { + "name": "Launch grpc", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}", + "args": [ + "--log-level", + "1", + "start", + "--network", + "tcp", + "--socket", + ":9547", + "--devmode" + ], + "env": { + "KUBECONFIG": "PATH_TO_YOUR_KIND_CONFIG", + } + } + ] +} + +---- diff --git a/docs/modules/ROOT/pages/explanations/.gitkeep b/docs/modules/ROOT/pages/explanations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/modules/ROOT/pages/explanations/apiserver/boostrap.adoc b/docs/modules/ROOT/pages/explanations/apiserver/boostrap.adoc new file mode 100644 index 0000000..b666c1b --- /dev/null +++ b/docs/modules/ROOT/pages/explanations/apiserver/boostrap.adoc @@ -0,0 +1,13 @@ += Bootstrap + +This repository was boostraped using https://github.com/kubernetes-sigs/apiserver-builder-alpha[apiserver-builder-alpha]. Due to a lot of unnecessary configuration the repository was cleaned and stripped down to a minimum configuration for AppCat needs. + +== Scaffolding +In order to achieve the scaffolding the following operations have been made: + +. Install apiserver-boot `go install sigs.k8s.io/apiserver-builder-alpha/cmd/apiserver-boot@v1.23.0` +. Save binary in system path +. Initialize the repository `apiserver-boot init repo --domain vshn.io` +. Create API `apiserver-boot create group version resource --group appcat --version v1 --kind AppCat --non-namespaced` +. Clean go dependencies `go mod tidy` +. Generate necessary boilerplate code `make generate` \ No newline at end of file diff --git a/docs/modules/ROOT/pages/how-tos/.gitkeep b/docs/modules/ROOT/pages/how-tos/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc new file mode 100644 index 0000000..08700f2 --- /dev/null +++ b/docs/modules/ROOT/pages/index.adoc @@ -0,0 +1,22 @@ += {page-component-name} + +[discrete] +== Introduction + +The Application Catalog is a suite of applications offered by https://www.vshn.ch[VSHN]. + +Here you’ll find all the technical documentation. +Use the search field at the top of this page, navigate through topics with the tree on the left or see the links below. + +[discrete] +== Documentation + +The documentation is inspired by the https://documentation.divio.com/[Divio's documentation structure]: + +Tutorials:: _Learning-oriented_: Simple lessons to learn about this project. + +How-to guides:: _Problem-oriented_: step-by-step guides to achieve a goal. + +Technical reference:: _Information-oriented_: explaining the inner ongoings. + +Explanation:: _Understanding-oriented_: puts this project in context. diff --git a/docs/modules/ROOT/pages/references/.gitkeep b/docs/modules/ROOT/pages/references/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/modules/ROOT/pages/references/apiserver/env-variables.adoc b/docs/modules/ROOT/pages/references/apiserver/env-variables.adoc new file mode 100644 index 0000000..c261869 --- /dev/null +++ b/docs/modules/ROOT/pages/references/apiserver/env-variables.adoc @@ -0,0 +1,8 @@ +=== Environmental Variables + +--- + +*APPCAT_HANDLER_ENABLED* - default _"true"_ ; This env enables appcat handler responsible for listing appcats + +*VSHN_POSTGRES_BACKUP_HANDLER_ENABLED* - default _"false"_ ; This env enables listing of VSHNPostgreSQL backups, enable it only on clusters that supports VSHNPostgreSQL + diff --git a/docs/modules/ROOT/pages/tutorials/.gitkeep b/docs/modules/ROOT/pages/tutorials/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/modules/ROOT/partials/.gitkeep b/docs/modules/ROOT/partials/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/package-lock.json b/docs/package-lock.json new file mode 100644 index 0000000..e10a2a1 --- /dev/null +++ b/docs/package-lock.json @@ -0,0 +1,727 @@ +{ + "name": "docs", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "gh-pages": "4.0.0" + } + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/email-addresses": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", + "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", + "engines": { + "node": ">=4" + } + }, + "node_modules/filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "dependencies": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/gh-pages": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-4.0.0.tgz", + "integrity": "sha512-p8S0T3aGJc68MtwOcZusul5qPSNZCalap3NWbhRUZYu1YOdp+EjZ+4kPmRM8h3NNRdqw00yuevRjlkuSzCn7iQ==", + "dependencies": { + "async": "^2.6.1", + "commander": "^2.18.0", + "email-addresses": "^3.0.1", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^8.1.0", + "globby": "^6.1.0" + }, + "bin": { + "gh-pages": "bin/gh-pages.js", + "gh-pages-clean": "bin/gh-pages-clean.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "requires": { + "lodash": "^4.17.14" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "email-addresses": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", + "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=" + }, + "filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "requires": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "gh-pages": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-4.0.0.tgz", + "integrity": "sha512-p8S0T3aGJc68MtwOcZusul5qPSNZCalap3NWbhRUZYu1YOdp+EjZ+4kPmRM8h3NNRdqw00yuevRjlkuSzCn7iQ==", + "requires": { + "async": "^2.6.1", + "commander": "^2.18.0", + "email-addresses": "^3.0.1", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^8.1.0", + "globby": "^6.1.0" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, + "trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000..d90338c --- /dev/null +++ b/docs/package.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "gh-pages": "5.0.0" + }, + "scripts": { + "deploy": "gh-pages -d ../.public --dotfiles -m \"Update documentation rev $(git rev-parse --short HEAD), $(date --utc '+%Y-%m-%d %H:%M')\"" + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2c4d895 --- /dev/null +++ b/go.mod @@ -0,0 +1,159 @@ +module github.com/vshn/appcat-apiserver + +go 1.20 + +replace github.com/deepmap/oapi-codegen => github.com/sljeff/oapi-codegen v1.5.1-0.20211207091501-bc20f55d338a + +require ( + github.com/crossplane/crossplane v1.11.5 + github.com/deepmap/oapi-codegen v0.0.0-00010101000000-000000000000 + github.com/go-logr/logr v1.2.3 + github.com/go-logr/zapr v1.2.3 + github.com/gogo/protobuf v1.3.2 + github.com/golang/mock v1.6.0 + github.com/golang/protobuf v1.5.3 + github.com/k8up-io/k8up/v2 v2.7.1 + github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.63.0 + github.com/spf13/cobra v1.7.0 + github.com/spf13/viper v1.15.0 + github.com/stretchr/testify v1.8.2 + github.com/vektra/mockery/v2 v2.26.1 + go.uber.org/zap v1.24.0 + golang.org/x/text v0.9.0 + gotest.tools/v3 v3.0.3 + k8s.io/api v0.26.3 + k8s.io/apimachinery v0.26.3 + k8s.io/apiserver v0.26.3 + k8s.io/client-go v0.26.3 + k8s.io/code-generator v0.26.3 + k8s.io/utils v0.0.0-20230313181309-38a27ef9d749 + sigs.k8s.io/apiserver-runtime v1.1.2-0.20221226021050-33c901856927 + sigs.k8s.io/controller-runtime v0.14.5 + sigs.k8s.io/controller-tools v0.11.3 + sigs.k8s.io/kind v0.17.0 +) + +require ( + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/crossplane/crossplane-runtime v0.19.2 // indirect + github.com/onsi/ginkgo/v2 v2.8.3 // indirect + github.com/onsi/gomega v1.27.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/common v0.40.0 // indirect + google.golang.org/grpc v1.55.0 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) + +require ( + github.com/BurntSushi/toml v1.2.1 // indirect + github.com/NYTimes/gziphandler v1.1.1 // indirect + github.com/alessio/shellescape v1.4.1 // indirect + github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chigopher/pathlib v0.13.0 // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/getkin/kin-openapi v0.107.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/swag v0.21.1 // indirect + github.com/gobuffalo/flect v0.3.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/google/cel-go v0.12.6 // indirect + github.com/google/gnostic v0.6.9 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/iancoleman/strcase v0.2.0 // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/invopop/yaml v0.1.0 // indirect + github.com/jinzhu/copier v0.3.5 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/labstack/echo/v4 v4.10.2 // indirect + github.com/labstack/gommon v0.4.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect + github.com/rs/zerolog v1.29.0 // indirect + github.com/sirupsen/logrus v1.9.2 // indirect + github.com/spf13/afero v1.9.3 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + go.etcd.io/etcd/api/v3 v3.5.6 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.6 // indirect + go.etcd.io/etcd/client/v3 v3.5.6 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.36.4 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.35.0 // indirect + go.opentelemetry.io/otel v1.11.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0 // indirect + go.opentelemetry.io/otel/metric v0.33.0 // indirect + go.opentelemetry.io/otel/sdk v1.10.0 // indirect + go.opentelemetry.io/otel/trace v1.11.1 // indirect + go.opentelemetry.io/proto/otlp v0.19.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/mod v0.10.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/sync v0.2.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/term v0.8.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.9.1 // indirect + gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiextensions-apiserver v0.26.3 // indirect + k8s.io/component-base v0.26.3 // indirect + k8s.io/gengo v0.0.0-20220902162205-c0856e24416d // indirect + k8s.io/klog/v2 v2.90.1 // indirect + k8s.io/kms v0.26.3 // indirect + k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.36 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..60d0c7b --- /dev/null +++ b/go.sum @@ -0,0 +1,1011 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= +github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= +github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chigopher/pathlib v0.13.0 h1:9AYqYGR+JaYJtZfTSsC+Wvz7CBd2jcZFb4fva7Z4r+4= +github.com/chigopher/pathlib v0.13.0/go.mod h1:EJ5UtJ/sK8Nt6q3VWN+EwZLZ3g0afJiG8NegYiQQ/gQ= +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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +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/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/crossplane/crossplane v1.11.5 h1:It/kq9VeO2dLy9kU95RJDJup9N0WEqiATmVMqcOci6Y= +github.com/crossplane/crossplane v1.11.5/go.mod h1:HhJ/EVMdbbZD5NuwJ32F1TSjaaiD6jPveP/qNghZiyk= +github.com/crossplane/crossplane-runtime v0.19.2 h1:9qBnhpqKN4x6apF2siaQ6PvgxqBXbGcKmgeD8mSIDO8= +github.com/crossplane/crossplane-runtime v0.19.2/go.mod h1:OJQ1NxtQK2ZTRmvtnQPoy8LsXsARTnVydRVDQEgIuz4= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= +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/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +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/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/getkin/kin-openapi v0.80.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/getkin/kin-openapi v0.107.0 h1:bxhL6QArW7BXQj8NjXfIJQy680NsMKd25nwhvpCXchg= +github.com/getkin/kin-openapi v0.107.0/go.mod h1:9Dhr+FasATJZjS4iOLvB0hkaxgYdulrNYm2e9epLWOo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= +github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= +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= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +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.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk= +github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE= +github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +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/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +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= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= +github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= +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/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/cel-go v0.12.6 h1:kjeKudqV0OygrAqA9fX6J55S8gj+Jre2tckIm5RoG4M= +github.com/google/cel-go v0.12.6/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20221102093814-76f304f74e5e h1:F1LLQqQ8WoIbyoxLUY+JUZe1kuHdxThM6CPUATzE6Io= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 h1:SJ+NtwL6QaZ21U+IrK7d0gGgpjGGvd2kz+FzTHVzdqI= +github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2/go.mod h1:Tv1PlzqC9t8wNnpPdctvtSUOPUUg4SHeE6vR1Ir2hmg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/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/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.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.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +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 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +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.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= +github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= +github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +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.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/k8up-io/k8up/v2 v2.7.1 h1:Ejbkb2bAfOKWAxmKOo3mGZKRx3Oz1oEahNfWTmiaUJA= +github.com/k8up-io/k8up/v2 v2.7.1/go.mod h1:xr0QtV800Zd55/XAQhDTamj1ulkENao1FpFDvjKYRT8= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/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.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/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= +github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= +github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= +github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= +github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= +github.com/lestrrat-go/codegen v1.0.2/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM= +github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE= +github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= +github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA= +github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +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.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +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/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo/v2 v2.8.3 h1:RpbK1G8nWPNaCVFBWsOGnEQQGgASi6b8fxcWBvDYjxQ= +github.com/onsi/ginkgo/v2 v2.8.3/go.mod h1:6OaUA8BCi0aZfmzYT/q9AacwTzDpNbxILUT+TlBq6MY= +github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754= +github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +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/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +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/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.63.0 h1:efsW3CfymG5bZUpeIsYfdihB33YItCn7uHBOEbnHQG8= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.63.0/go.mod h1:/UtstAaWVaS3Z9GK9jo8+4SN9T+RMSq7VlOcQMmiEsc= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.40.0 h1:Afz7EVRqGg2Mqqf4JuF9vdvp1pi220m55Pi9T2JnO4Q= +github.com/prometheus/common v0.40.0/go.mod h1:L65ZJPSmfn/UBWLQIHV7dBrKFidB/wPlF1y5TlSt9OE= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +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/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= +github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +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.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= +github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sljeff/oapi-codegen v1.5.1-0.20211207091501-bc20f55d338a h1:bLe3hVqcJrse1CGVS2MtkPK139RaGVdhIGY3wNb0eFQ= +github.com/sljeff/oapi-codegen v1.5.1-0.20211207091501-bc20f55d338a/go.mod h1:Vid3nS1BbbSPSqaGOuMCEU1Ml8R/8VLjcyZZGDCKkkY= +github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.4.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= +github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag 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.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= +github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/vektra/mockery/v2 v2.26.1 h1:Y8mlLkWHWjuUpsJBwhFb1LeG1ZnWFvo+prsIuiABJ88= +github.com/vektra/mockery/v2 v2.26.1/go.mod h1:BOVUIv65DB6wuTYzoPtyMoBYce3n2C1IcsOdWu6Rpu4= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= +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/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= +go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c h1:/RwRVN9EdXAVtdHxP7Ndn/tfmM9/goiwU0QTnLBgS4w= +go.etcd.io/etcd/api/v3 v3.5.6 h1:Cy2qx3npLcYqTKqGJzMypnMv2tiRyifZJ17BlWIWA7A= +go.etcd.io/etcd/api/v3 v3.5.6/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= +go.etcd.io/etcd/client/pkg/v3 v3.5.6 h1:TXQWYceBKqLp4sa87rcPs11SXxUA/mHwH975v+BDvLU= +go.etcd.io/etcd/client/pkg/v3 v3.5.6/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= +go.etcd.io/etcd/client/v2 v2.305.6 h1:fIDR0p4KMjw01MJMfUIDWdQbjo06PD6CeYM5z4EHLi0= +go.etcd.io/etcd/client/v3 v3.5.6 h1:coLs69PWCXE9G4FKquzNaSHrRyMCAXwF+IX1tAPVO8E= +go.etcd.io/etcd/client/v3 v3.5.6/go.mod h1:f6GRinRMCsFVv9Ht42EyY7nfsVGwrNO0WEoS2pRKzQk= +go.etcd.io/etcd/pkg/v3 v3.5.5 h1:Ablg7T7OkR+AeeeU32kdVhw/AGDsitkKPl7aW73ssjU= +go.etcd.io/etcd/raft/v3 v3.5.5 h1:Ibz6XyZ60OYyRopu73lLM/P+qco3YtlZMOhnXNS051I= +go.etcd.io/etcd/server/v3 v3.5.5 h1:jNjYm/9s+f9A9r6+SC4RvNaz6AqixpOvhrFdT0PvIj0= +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.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/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.36.4 h1:PRXhsszxTt5bbPriTjmaweWUsAnJYeWBhUMLRetUgBU= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.36.4/go.mod h1:05eWWy6ZWzmpeImD3UowLTB3VjDMU1yxQ+ENuVWDM3c= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.35.0 h1:Ajldaqhxqw/gNzQA45IKFWLdG7jZuXX/wBW1d5qvbUI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.35.0/go.mod h1:9NiG9I2aHTKkcxqCILhjtyNA1QEiCjdBACv4IvrFQ+c= +go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= +go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0 h1:TaB+1rQhddO1sF71MpZOZAuSPW1klK2M8XxfrBMfK7Y= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0 h1:pDDYmo0QadUPal5fwXoY1pmMpFcdyhXOmL5drCrI3vU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0/go.mod h1:Krqnjl22jUJ0HgMzw5eveuCvFDXY4nSYb4F8t5gdrag= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0 h1:KtiUEhQmj/Pa874bVYKGNVdq8NPKiacPbaRRtgXi+t4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0/go.mod h1:OfUCyyIiDvNXHWpcWgbF+MWvqPZiNa3YDEnivcnYsV0= +go.opentelemetry.io/otel/metric v0.33.0 h1:xQAyl7uGEYvrLAiV/09iTJlp1pZnQ9Wl793qbVvED1E= +go.opentelemetry.io/otel/metric v0.33.0/go.mod h1:QlTYc+EnYNq/M2mNk1qDDMRLpqCOj2f/r5c7Fd5FYaI= +go.opentelemetry.io/otel/sdk v1.10.0 h1:jZ6K7sVn04kk/3DNUdJ4mqRlGDiXAVuIG+MMENpTNdY= +go.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE= +go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= +go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +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/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/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-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-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-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= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +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-20190604053449-0f29369cfe45/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/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-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-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-20190606165138-5da285871e9c/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-20191001151750-bb3f8db39f24/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-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +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= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +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-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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-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= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +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-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/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-20190816200558-6889da9d5479/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-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/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-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +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-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= +gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +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.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= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/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= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +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-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +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= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +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.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU= +k8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE= +k8s.io/apiextensions-apiserver v0.26.3 h1:5PGMm3oEzdB1W/FTMgGIDmm100vn7IaUP5er36dB+YE= +k8s.io/apiextensions-apiserver v0.26.3/go.mod h1:jdA5MdjNWGP+njw1EKMZc64xAT5fIhN6VJrElV3sfpQ= +k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k= +k8s.io/apimachinery v0.26.3/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= +k8s.io/apiserver v0.26.3 h1:blBpv+yOiozkPH2aqClhJmJY+rp53Tgfac4SKPDJnU4= +k8s.io/apiserver v0.26.3/go.mod h1:CJe/VoQNcXdhm67EvaVjYXxR3QyfwpceKPuPaeLibTA= +k8s.io/client-go v0.26.3 h1:k1UY+KXfkxV2ScEL3gilKcF7761xkYsSD6BC9szIu8s= +k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ= +k8s.io/code-generator v0.26.3 h1:DNYPsWoeFwmg4qFg97Z1cHSSv7KSG10mAEIFoZGTQM8= +k8s.io/code-generator v0.26.3/go.mod h1:ryaiIKwfxEJEaywEzx3dhWOydpVctKYbqLajJf0O8dI= +k8s.io/component-base v0.26.3 h1:oC0WMK/ggcbGDTkdcqefI4wIZRYdK3JySx9/HADpV0g= +k8s.io/component-base v0.26.3/go.mod h1:5kj1kZYwSC6ZstHJN7oHBqcJC6yyn41eR+Sqa/mQc8E= +k8s.io/gengo v0.0.0-20220902162205-c0856e24416d h1:U9tB195lKdzwqicbJvyJeOXV7Klv+wNAWENRnXEGi08= +k8s.io/gengo v0.0.0-20220902162205-c0856e24416d/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kms v0.26.3 h1:+rC4BMeMBkH5hrfZt9WFMRrs2m3vY2rXymisNactcTY= +k8s.io/kms v0.26.3/go.mod h1:69qGnf1NsFOQP07fBYqNLZklqEHSJF024JqYCaeVxHg= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/utils v0.0.0-20230313181309-38a27ef9d749 h1:xMMXJlJbsU8w3V5N2FLDQ8YgU8s1EoULdbQBcAeNJkY= +k8s.io/utils v0.0.0-20230313181309-38a27ef9d749/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +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.36 h1:PUuX1qIFv309AT8hF/CdPKDmsG/hn/L8zRX7VvISM3A= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.36/go.mod h1:WxjusMwXlKzfAs4p9km6XJRndVt2FROgMVCE4cdohFo= +sigs.k8s.io/apiserver-runtime v1.1.2-0.20221226021050-33c901856927 h1:YLqdUEPYZ5jzmORSZeRs5AZWFRJbZ0jrLivh3/X145M= +sigs.k8s.io/apiserver-runtime v1.1.2-0.20221226021050-33c901856927/go.mod h1:4MJyV8pBRl+X7ml5z/D5H0AVJyz9qb49MIt0295HWNA= +sigs.k8s.io/controller-runtime v0.14.5 h1:6xaWFqzT5KuAQ9ufgUaj1G/+C4Y1GRkhrxl+BJ9i+5s= +sigs.k8s.io/controller-runtime v0.14.5/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= +sigs.k8s.io/controller-tools v0.11.3 h1:T1xzLkog9saiyQSLz1XOImu4OcbdXWytc5cmYsBeBiE= +sigs.k8s.io/controller-tools v0.11.3/go.mod h1:qcfX7jfcfYD/b7lAhvqAyTbt/px4GpvN88WKLFFv7p8= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/kind v0.17.0 h1:CScmGz/wX66puA06Gj8OZb76Wmk7JIjgWf5JDvY7msM= +sigs.k8s.io/kind v0.17.0/go.mod h1:Qqp8AiwOlMZmJWs37Hgs31xcbiYXjtXlRBSftcnZXQk= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/log.go b/log.go new file mode 100644 index 0000000..94461b9 --- /dev/null +++ b/log.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + "github.com/go-logr/logr" + "github.com/go-logr/zapr" + "github.com/spf13/cobra" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "os" + "runtime" + "strings" + "time" +) + +// LogMetadata prints various metadata to the root logger. +// It prints version, architecture and current user ID and returns nil. +func LogMetadata(cmd *cobra.Command) error { + log := logr.FromContextOrDiscard(cmd.Context()) + log.WithValues( + "date", time.Now(), + "go_os", runtime.GOOS, + "go_arch", runtime.GOARCH, + "go_version", runtime.Version(), + "uid", os.Getuid(), + "gid", os.Getgid(), + ).Info("Starting up " + cmd.Short) + return nil +} + +func SetupLogging(cmd *cobra.Command, logLevel int, logFormat string) error { + isJson := strings.EqualFold("JSON", logFormat) + log, err := newZapLogger(strings.ToUpper(cmd.Use), logLevel, isJson) + cmd.SetContext(logr.NewContext(cmd.Context(), log)) + return err +} + +func newZapLogger(name string, verbosityLevel int, useProductionConfig bool) (logr.Logger, error) { + cfg := zap.NewDevelopmentConfig() + cfg.EncoderConfig.ConsoleSeparator = " | " + if useProductionConfig { + cfg = zap.NewProductionConfig() + } + // Zap's levels get more verbose as the number gets smaller, + // bug logr's level increases with greater numbers. + cfg.Level = zap.NewAtomicLevelAt(zapcore.Level(verbosityLevel * -1)) + z, err := cfg.Build() + if err != nil { + return logr.Discard(), fmt.Errorf("error configuring the logging stack: %w", err) + } + zap.ReplaceGlobals(z) + zlog := zapr.NewLogger(z).WithName(name) + return zlog, nil +} diff --git a/login.json b/login.json new file mode 100644 index 0000000..2133869 --- /dev/null +++ b/login.json @@ -0,0 +1,4 @@ +{ + "password": "f0QOZutJ8aRUWiy9pww0WHpHfYk5OiImSieflB1b", + "username": "admin" +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..4ed30ba --- /dev/null +++ b/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/vshn/appcat-apiserver/cmd" +) + +func init() { + rootCmd.AddCommand(cmd.APIServerCMD) +} + +func main() { + if err := rootCmd.Execute(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +var ( + logLevel int + logFormat string + rootCmd = &cobra.Command{ + Short: "AppCat", + Long: "AppCat controllers, api servers, grpc servers and probers", + PersistentPreRunE: setupLogging, + } +) + +func init() { + rootCmd.PersistentFlags().IntVarP(&logLevel, "log-level", "v", 0, "Number of the log level verbosity.") + rootCmd.PersistentFlags().StringVar(&logFormat, "log-format", "console", "Sets the log format (values: [json, console]).") + viper.AutomaticEnv() +} + +func setupLogging(cmd *cobra.Command, _ []string) error { + err := SetupLogging(cmd, logLevel, logFormat) + if err != nil { + return err + } + + return LogMetadata(cmd) +} diff --git a/pkg/apiserver/appcat/appcat.go b/pkg/apiserver/appcat/appcat.go new file mode 100644 index 0000000..3eab31f --- /dev/null +++ b/pkg/apiserver/appcat/appcat.go @@ -0,0 +1,57 @@ +package appcat + +import ( + crossplane "github.com/crossplane/crossplane/apis/apiextensions/v1" + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + "k8s.io/apimachinery/pkg/runtime" + genericregistry "k8s.io/apiserver/pkg/registry/generic" + "k8s.io/apiserver/pkg/registry/rest" + restbuilder "sigs.k8s.io/apiserver-runtime/pkg/builder/rest" + "sigs.k8s.io/apiserver-runtime/pkg/util/loopback" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch,resourceNames=extension-apiserver-authentication +// +kubebuilder:rbac:groups="admissionregistration.k8s.io",resources=mutatingwebhookconfigurations;validatingwebhookconfigurations,verbs=get;list;watch +// +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch;create;delete;update +// +kubebuilder:rbac:groups="authorization.k8s.io",resources=subjectaccessreviews,verbs=get;list;watch;create;delete;update + +// New returns a new storage provider for AppCat +func New() restbuilder.ResourceHandlerProvider { + return func(s *runtime.Scheme, gasdf genericregistry.RESTOptionsGetter) (rest.Storage, error) { + c, err := client.NewWithWatch(loopback.GetLoopbackMasterClientConfig(), client.Options{}) + if err != nil { + return nil, err + } + err = v1.AddToScheme(c.Scheme()) + if err != nil { + return nil, err + } + err = crossplane.AddToScheme(c.Scheme()) + if err != nil { + return nil, err + } + return &appcatStorage{ + compositions: &kubeCompositionProvider{ + Client: c, + }, + }, nil + } +} + +type appcatStorage struct { + compositions compositionProvider +} + +func (s *appcatStorage) New() runtime.Object { + return &v1.AppCat{} +} + +func (s *appcatStorage) Destroy() {} + +var _ rest.Scoper = &appcatStorage{} +var _ rest.Storage = &appcatStorage{} + +func (s *appcatStorage) NamespaceScoped() bool { + return false +} diff --git a/pkg/apiserver/appcat/appcat_test.go b/pkg/apiserver/appcat/appcat_test.go new file mode 100644 index 0000000..ee9c595 --- /dev/null +++ b/pkg/apiserver/appcat/appcat_test.go @@ -0,0 +1,92 @@ +package appcat + +import ( + "testing" + + crossplanev1 "github.com/crossplane/crossplane/apis/apiextensions/v1" + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + "github.com/vshn/appcat-apiserver/test/mocks" + "k8s.io/apiserver/pkg/registry/rest" + + "github.com/golang/mock/gomock" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// newMockedAppCatStorage is a mocked instance of AppCatStorage +func newMockedAppCatStorage(t *testing.T, ctrl *gomock.Controller) (rest.StandardStorage, *mocks.MockcompositionProvider) { + t.Helper() + comp := mocks.NewMockcompositionProvider(ctrl) + stor := &appcatStorage{ + compositions: comp, + } + return rest.Storage(stor).(rest.StandardStorage), comp +} + +// Test AppCat instances +var ( + appCatOne = &v1.AppCat{ + ObjectMeta: metav1.ObjectMeta{ + Name: "one", + }, + + Details: map[string]string{ + "zone": "rma1", + "displayname": "one", + "docs": "https://docs.com", + }, + + Status: v1.AppCatStatus{ + CompositionName: "one", + }, + } + compositionOne = &crossplanev1.Composition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "one", + Labels: map[string]string{ + v1.OfferedKey: v1.OfferedValue, + }, + Annotations: map[string]string{ + v1.PrefixAppCatKey + "/zone": "rma1", + v1.PrefixAppCatKey + "/displayname": "one", + v1.PrefixAppCatKey + "/docs": "https://docs.com", + }, + }, + } + appCatTwo = &v1.AppCat{ + ObjectMeta: metav1.ObjectMeta{ + Name: "two", + }, + + Details: map[string]string{ + "zone": "lpg", + "displayname": "two", + "docs": "https://docs.com", + "productDescription": "product desc", + }, + + Status: v1.AppCatStatus{ + CompositionName: "two", + }, + } + compositionTwo = &crossplanev1.Composition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "two", + Labels: map[string]string{ + v1.OfferedKey: v1.OfferedValue, + }, + Annotations: map[string]string{ + v1.PrefixAppCatKey + "/zone": "lpg", + v1.PrefixAppCatKey + "/displayname": "two", + v1.PrefixAppCatKey + "/docs": "https://docs.com", + v1.PrefixAppCatKey + "/product-description": "product desc", + }, + }, + } + compositionNonOffered = &crossplanev1.Composition{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + v1.OfferedKey: "false", + }, + }, + } +) diff --git a/pkg/apiserver/appcat/composition.go b/pkg/apiserver/appcat/composition.go new file mode 100644 index 0000000..07491c1 --- /dev/null +++ b/pkg/apiserver/appcat/composition.go @@ -0,0 +1,51 @@ +package appcat + +import ( + "context" + v1 "github.com/crossplane/crossplane/apis/apiextensions/v1" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// compositionProvider is an abstraction to interact with the K8s API +type compositionProvider interface { + GetComposition(ctx context.Context, name string, options *metav1.GetOptions) (*v1.Composition, error) + ListCompositions(ctx context.Context, options *metainternalversion.ListOptions) (*v1.CompositionList, error) + WatchCompositions(ctx context.Context, options *metainternalversion.ListOptions) (watch.Interface, error) +} + +type kubeCompositionProvider struct { + Client client.WithWatch +} + +func (k *kubeCompositionProvider) GetComposition(ctx context.Context, name string, options *metav1.GetOptions) (*v1.Composition, error) { + c := v1.Composition{} + err := k.Client.Get(ctx, client.ObjectKey{Namespace: "", Name: name}, &c) + return &c, err +} + +func (k *kubeCompositionProvider) ListCompositions(ctx context.Context, options *metainternalversion.ListOptions) (*v1.CompositionList, error) { + cl := v1.CompositionList{} + err := k.Client.List(ctx, &cl, &client.ListOptions{ + LabelSelector: options.LabelSelector, + FieldSelector: options.FieldSelector, + Limit: options.Limit, + Continue: options.Continue, + }) + if err != nil { + return nil, err + } + return &cl, nil +} + +func (k *kubeCompositionProvider) WatchCompositions(ctx context.Context, options *metainternalversion.ListOptions) (watch.Interface, error) { + cl := v1.CompositionList{} + return k.Client.Watch(ctx, &cl, &client.ListOptions{ + LabelSelector: options.LabelSelector, + FieldSelector: options.FieldSelector, + Limit: options.Limit, + Continue: options.Continue, + }) +} diff --git a/pkg/apiserver/appcat/create.go b/pkg/apiserver/appcat/create.go new file mode 100644 index 0000000..b01d562 --- /dev/null +++ b/pkg/apiserver/appcat/create.go @@ -0,0 +1,15 @@ +package appcat + +import ( + "context" + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Creater = &appcatStorage{} + +func (s *appcatStorage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) { + return nil, fmt.Errorf("method not implemented") +} diff --git a/pkg/apiserver/appcat/delete.go b/pkg/apiserver/appcat/delete.go new file mode 100644 index 0000000..266448f --- /dev/null +++ b/pkg/apiserver/appcat/delete.go @@ -0,0 +1,29 @@ +package appcat + +import ( + "context" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.GracefulDeleter = &appcatStorage{} +var _ rest.CollectionDeleter = &appcatStorage{} + +func (s *appcatStorage) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) { + return &v1.AppCat{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + }, false, nil +} + +func (s *appcatStorage) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *metainternalversion.ListOptions) (runtime.Object, error) { + return &v1.AppCatList{ + Items: []v1.AppCat{}, + }, nil +} diff --git a/pkg/apiserver/appcat/get.go b/pkg/apiserver/appcat/get.go new file mode 100644 index 0000000..02df13b --- /dev/null +++ b/pkg/apiserver/appcat/get.go @@ -0,0 +1,30 @@ +package appcat + +import ( + "context" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + "github.com/vshn/appcat-apiserver/pkg/apiserver" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Getter = &appcatStorage{} + +// Get returns an AppCat service based on its composition +func (s *appcatStorage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { + composition, err := s.compositions.GetComposition(ctx, name, options) + if err != nil { + return nil, apiserver.ResolveError(v1.GetGroupResource(v1.Resource), err) + } + + appcat := v1.NewAppCatFromComposition(composition) + if appcat == nil { + // This composition is not an AppCat service + return nil, apierrors.NewNotFound(appcat.GetGroupVersionResource().GroupResource(), name) + } + + return appcat, nil +} diff --git a/pkg/apiserver/appcat/get_test.go b/pkg/apiserver/appcat/get_test.go new file mode 100644 index 0000000..ca5e35a --- /dev/null +++ b/pkg/apiserver/appcat/get_test.go @@ -0,0 +1,86 @@ +package appcat + +import ( + "testing" + + crossplanev1 "github.com/crossplane/crossplane/apis/apiextensions/v1" + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "k8s.io/apiserver/pkg/endpoints/request" +) + +func TestAppCatStorage_Get(t *testing.T) { + tests := map[string]struct { + name string + composition *crossplanev1.Composition + compErr error + appcat *v1.AppCat + err error + }{ + "GivenAComposition_ThenAppCat": { + name: "one", + composition: compositionOne, + appcat: appCatOne, + }, + "GivenErrNotFound_ThenErrNotFound": { + name: "not-found", + compErr: apierrors.NewNotFound(schema.GroupResource{ + Resource: "compositions", + }, "not-found"), + err: apierrors.NewNotFound(schema.GroupResource{ + Group: v1.GroupVersion.Group, + Resource: "appcats", + }, "not-found"), + }, + "GivenNonAppCatComp_ThenErrNotFound": { + name: "appcat-not-found", + composition: &crossplanev1.Composition{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + v1.OfferedKey: "false", + }, + }, + }, + err: apierrors.NewNotFound(schema.GroupResource{ + Group: v1.GroupVersion.Group, + Resource: "appcats", + }, "appcat-not-found"), + }, + } + + for n, tc := range tests { + + t.Run(n, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + stor, compProvider := newMockedAppCatStorage(t, ctrl) + + compProvider.EXPECT(). + GetComposition(gomock.Any(), tc.name, gomock.Any()). + Return(tc.composition, tc.compErr). + Times(1) + + appcat, err := stor.Get(request.WithRequestInfo(request.NewContext(), + &request.RequestInfo{ + Verb: "get", + APIGroup: v1.GroupVersion.Group, + Resource: "appcats", + Name: tc.name, + }), + tc.name, nil) + if tc.err != nil { + assert.EqualError(t, err, tc.err.Error()) + return + } + require.NoError(t, err) + assert.Equal(t, tc.appcat, appcat) + }) + } +} diff --git a/pkg/apiserver/appcat/list.go b/pkg/apiserver/appcat/list.go new file mode 100644 index 0000000..23a9954 --- /dev/null +++ b/pkg/apiserver/appcat/list.go @@ -0,0 +1,89 @@ +package appcat + +import ( + "context" + + crossplanev1 "github.com/crossplane/crossplane/apis/apiextensions/v1" + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + "github.com/vshn/appcat-apiserver/pkg/apiserver" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/selection" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Lister = &appcatStorage{} + +func (s *appcatStorage) NewList() runtime.Object { + return &v1.AppCatList{} +} + +// List returns a list of AppCat services based on their compositions +func (s *appcatStorage) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) { + cl, err := s.compositions.ListCompositions(ctx, addOfferedLabelSelector(options)) + if err != nil { + return nil, apiserver.ResolveError(v1.GetGroupResource(v1.Resource), err) + } + + res := v1.AppCatList{ + ListMeta: cl.ListMeta, + } + + for _, v := range cl.Items { + appCat := v1.NewAppCatFromComposition(&v) + if appCat != nil { + res.Items = append(res.Items, *appCat) + } + } + + return &res, nil +} + +var _ rest.Watcher = &appcatStorage{} + +// Watch returns a watched list of AppCat services based on their compositions +func (s *appcatStorage) Watch(ctx context.Context, options *metainternalversion.ListOptions) (watch.Interface, error) { + compWatcher, err := s.compositions.WatchCompositions(ctx, addOfferedLabelSelector(options)) + if err != nil { + return nil, apiserver.ResolveError(v1.GetGroupResource(v1.Resource), err) + } + + return watch.Filter(compWatcher, func(in watch.Event) (out watch.Event, keep bool) { + if in.Object == nil { + // This should never happen, let downstream deal with it + return in, true + } + comp, ok := in.Object.(*crossplanev1.Composition) + if !ok { + // We received a non Composition object + // This is most likely an error so we pass it on + return in, true + } + + in.Object = v1.NewAppCatFromComposition(comp) + if in.Object.(*v1.AppCat) == nil { + return in, false + } + + return in, true + }), nil +} + +func addOfferedLabelSelector(options *metainternalversion.ListOptions) *metainternalversion.ListOptions { + offeredComposition, err := labels.NewRequirement(v1.OfferedKey, selection.Equals, []string{v1.OfferedValue}) + if err != nil { + // The input is static. This call will only fail during development. + panic(err) + } + if options == nil { + options = &metainternalversion.ListOptions{} + } + if options.LabelSelector == nil { + options.LabelSelector = labels.NewSelector() + } + options.LabelSelector = options.LabelSelector.Add(*offeredComposition) + + return options +} diff --git a/pkg/apiserver/appcat/list_test.go b/pkg/apiserver/appcat/list_test.go new file mode 100644 index 0000000..2413dc3 --- /dev/null +++ b/pkg/apiserver/appcat/list_test.go @@ -0,0 +1,205 @@ +package appcat + +import ( + "testing" + + crossplanev1 "github.com/crossplane/crossplane/apis/apiextensions/v1" + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "k8s.io/apiserver/pkg/endpoints/request" +) + +func TestAppcatStorage_List(t *testing.T) { + tests := map[string]struct { + compositions *crossplanev1.CompositionList + compositionErr error + + appcats *v1.AppCatList + err error + }{ + "GivenListOfCompositions_ThenReturnAppCats": { + compositions: &crossplanev1.CompositionList{ + Items: []crossplanev1.Composition{ + *compositionOne, + *compositionTwo, + }, + }, + appcats: &v1.AppCatList{ + Items: []v1.AppCat{ + *appCatOne, + *appCatTwo, + }, + }, + }, + "GivenErrNotFound_ThenErrNotFound": { + compositionErr: apierrors.NewNotFound(schema.GroupResource{ + Resource: "compositions", + }, "not-found"), + err: apierrors.NewNotFound(schema.GroupResource{ + Group: v1.GroupVersion.Group, + Resource: v1.Resource, + }, "not-found"), + }, + "GivenList_ThenFilter": { + compositions: &crossplanev1.CompositionList{ + Items: []crossplanev1.Composition{ + *compositionOne, + *compositionNonOffered, + }, + }, + appcats: &v1.AppCatList{ + Items: []v1.AppCat{ + *appCatOne, + }, + }, + }, + } + for n, tc := range tests { + t.Run(n, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + stor, compProvider := newMockedAppCatStorage(t, ctrl) + + compProvider.EXPECT(). + ListCompositions(gomock.Any(), gomock.Any()). + Return(tc.compositions, tc.compositionErr). + Times(1) + + appcats, err := stor.List(request.WithRequestInfo(request.NewContext(), + &request.RequestInfo{ + Verb: "list", + APIGroup: v1.GroupVersion.Group, + Resource: v1.Resource, + }), nil) + if tc.err != nil { + assert.EqualError(t, err, tc.err.Error()) + return + } + require.NoError(t, err) + assert.Equal(t, tc.appcats, appcats) + }) + } +} + +type testWatcher struct { + events chan watch.Event +} + +func (w testWatcher) Stop() {} + +func (w testWatcher) ResultChan() <-chan watch.Event { + return w.events +} + +func TestAppCatsStorage_Watch(t *testing.T) { + tests := map[string]struct { + compositionEvents []watch.Event + compositionErr error + + appcatEvents []watch.Event + err error + }{ + "GivenCompositionEvents_ThenAppCatEvents": { + compositionEvents: []watch.Event{ + { + Type: watch.Added, + Object: compositionOne, + }, + { + Type: watch.Modified, + Object: compositionTwo, + }, + }, + appcatEvents: []watch.Event{ + { + Type: watch.Added, + Object: appCatOne, + }, + { + Type: watch.Modified, + Object: appCatTwo, + }, + }, + }, + "GivenErrNotFound_ThenErrNotFound": { + compositionErr: apierrors.NewNotFound(schema.GroupResource{ + Resource: "compositions", + }, "not-found"), + err: apierrors.NewNotFound(schema.GroupResource{ + Group: v1.GroupVersion.Group, + Resource: v1.Resource, + }, "not-found"), + }, + "GivenVariousCompositionEvents_ThenFilter": { + compositionEvents: []watch.Event{ + { + Type: watch.Added, + Object: compositionOne, + }, + { + Type: watch.Modified, + Object: compositionNonOffered, + }, + { + Type: watch.Modified, + Object: compositionTwo, + }, + }, + appcatEvents: []watch.Event{ + { + Type: watch.Added, + Object: appCatOne, + }, + { + Type: watch.Modified, + Object: appCatTwo, + }, + }, + }, + } + + for n, tc := range tests { + t.Run(n, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + stor, compProvider := newMockedAppCatStorage(t, ctrl) + + compWatcher := testWatcher{ + events: make(chan watch.Event, len(tc.compositionEvents)), + } + for _, e := range tc.compositionEvents { + compWatcher.events <- e + } + close(compWatcher.events) + + compProvider.EXPECT(). + WatchCompositions(gomock.Any(), gomock.Any()). + Return(compWatcher, tc.compositionErr). + AnyTimes() + + appcatWatch, err := stor.Watch(request.WithRequestInfo(request.NewContext(), + &request.RequestInfo{ + Verb: "watch", + APIGroup: v1.GroupVersion.Group, + Resource: v1.Resource, + }), nil) + if tc.err != nil { + assert.EqualError(t, err, tc.err.Error()) + return + } + require.NoError(t, err) + appcatEvents := []watch.Event{} + for e := range appcatWatch.ResultChan() { + appcatEvents = append(appcatEvents, e) + } + assert.Equal(t, tc.appcatEvents, appcatEvents) + }) + } +} diff --git a/pkg/apiserver/appcat/table.go b/pkg/apiserver/appcat/table.go new file mode 100644 index 0000000..d35f0dc --- /dev/null +++ b/pkg/apiserver/appcat/table.go @@ -0,0 +1,70 @@ +package appcat + +import ( + "context" + "fmt" + "time" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/duration" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.TableConvertor = &appcatStorage{} + +var ( + appCatDisplayname = "displayname" + appCatZone = "zone" + appCatDocs = "endUserDocsUrl" +) + +// ConvertToTable translates the given object to a table for kubectl printing +func (s *appcatStorage) ConvertToTable(_ context.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { + var table metav1.Table + + appcats := []v1.AppCat{} + if meta.IsListType(obj) { + appcatList, ok := obj.(*v1.AppCatList) + if !ok { + return nil, fmt.Errorf("not an appcat: %#v", obj) + } + appcats = appcatList.Items + } else { + appcat, ok := obj.(*v1.AppCat) + if !ok { + return nil, fmt.Errorf("not an appcat: %#v", obj) + } + appcats = append(appcats, *appcat) + } + + for _, appcat := range appcats { + table.Rows = append(table.Rows, appcatToTableRow(&appcat)) + } + + if opt, ok := tableOptions.(*metav1.TableOptions); !ok || !opt.NoHeaders { + desc := metav1.ObjectMeta{}.SwaggerDoc() + table.ColumnDefinitions = []metav1.TableColumnDefinition{ + {Name: "AppCat Name", Type: "string", Format: "name", Description: desc["name"]}, + {Name: "AppCat Display Name", Type: "string", Format: "name", Description: "The display name of the service"}, + {Name: "Service Zone", Type: "string", Description: "Available zones of the service"}, + {Name: "User Docs", Type: "string", Description: "The user documentation of the service"}, + {Name: "Age", Type: "date", Description: desc["creationTimestamp"]}, + } + } + return &table, nil +} + +func appcatToTableRow(appcat *v1.AppCat) metav1.TableRow { + return metav1.TableRow{ + Cells: []interface{}{ + appcat.GetName(), + appcat.Details[appCatDisplayname], + appcat.Details[appCatZone], + appcat.Details[appCatDocs], + duration.HumanDuration(time.Since(appcat.GetCreationTimestamp().Time))}, + Object: runtime.RawExtension{Object: appcat}, + } +} diff --git a/pkg/apiserver/appcat/table_test.go b/pkg/apiserver/appcat/table_test.go new file mode 100644 index 0000000..d90d6ff --- /dev/null +++ b/pkg/apiserver/appcat/table_test.go @@ -0,0 +1,74 @@ +package appcat + +import ( + "context" + "testing" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func TestAppCatStorage_ConvertToTable(t *testing.T) { + tests := map[string]struct { + obj runtime.Object + tableOptions runtime.Object + fail bool + nrRows int + }{ + "GivenEmptyAppCat_ThenSingleRow": { + obj: &v1.AppCat{}, + nrRows: 1, + }, + "GivenAppCat_ThenSingleRow": { + obj: &v1.AppCat{ + ObjectMeta: metav1.ObjectMeta{Name: "pippo"}, + + Details: map[string]string{ + "zone": "rma1", + "displayname": "ObjectStorage", + }, + }, + nrRows: 1, + }, + "GivenAppCatList_ThenMultipleRow": { + obj: &v1.AppCatList{ + Items: []v1.AppCat{ + {}, + {}, + {}, + }, + }, + nrRows: 3, + }, + "GivenNil_ThenFail": { + obj: nil, + fail: true, + }, + "GivenNonAppCat_ThenFail": { + obj: &corev1.Pod{}, + fail: true, + }, + "GivenNonAppCatList_ThenFail": { + obj: &corev1.PodList{}, + fail: true, + }, + } + appcatStore := &appcatStorage{} + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + table, err := appcatStore.ConvertToTable(context.TODO(), tc.obj, tc.tableOptions) + if tc.fail { + require.Error(t, err) + return + } + require.NoError(t, err) + assert.Len(t, table.Rows, tc.nrRows) + }) + } +} diff --git a/pkg/apiserver/appcat/update.go b/pkg/apiserver/appcat/update.go new file mode 100644 index 0000000..5a062b8 --- /dev/null +++ b/pkg/apiserver/appcat/update.go @@ -0,0 +1,21 @@ +package appcat + +import ( + "context" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Updater = &appcatStorage{} +var _ rest.CreaterUpdater = &appcatStorage{} + +func (s *appcatStorage) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) { + return &v1.AppCat{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + }, false, nil +} diff --git a/pkg/apiserver/common.go b/pkg/apiserver/common.go new file mode 100644 index 0000000..c258d85 --- /dev/null +++ b/pkg/apiserver/common.go @@ -0,0 +1,69 @@ +package apiserver + +import ( + "errors" + "sync" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" +) + +func ResolveError(groupResource schema.GroupResource, err error) error { + statusErr := &apierrors.StatusError{} + + if errors.As(err, &statusErr) { + switch { + case apierrors.IsNotFound(err): + return apierrors.NewNotFound(groupResource, statusErr.ErrStatus.Details.Name) + case apierrors.IsAlreadyExists(err): + return apierrors.NewAlreadyExists(groupResource, statusErr.ErrStatus.Details.Name) + } + } + return err +} + +// MultiWatch is wrapper of multiple source watches which implements the same methods as a normal watch.Watch +var _ watch.Interface = &MultiWatcher{} + +type MultiWatcher struct { + watchers []watch.Interface + eventChan chan watch.Event + wg sync.WaitGroup +} + +// NewEmptyMultiWatch creates an empty watch +func NewEmptyMultiWatch() *MultiWatcher { + return &MultiWatcher{ + eventChan: make(chan watch.Event), + } +} + +// AddWatcher adds a watch to this MultiWatcher +func (m *MultiWatcher) AddWatcher(w watch.Interface) { + m.watchers = append(m.watchers, w) +} + +// Stop stops all watches of this MultiWatch +func (m *MultiWatcher) Stop() { + for _, watcher := range m.watchers { + watcher.Stop() + } + m.wg.Wait() + close(m.eventChan) +} + +// ResultChan aggregates all channels from all watches of this MultiWatch +func (m *MultiWatcher) ResultChan() <-chan watch.Event { + for _, w := range m.watchers { + m.wg.Add(1) + watcher := w + go func() { + defer m.wg.Done() + for c := range watcher.ResultChan() { + m.eventChan <- c + } + }() + } + return m.eventChan +} diff --git a/pkg/apiserver/hack/boilerplate.txt b/pkg/apiserver/hack/boilerplate.txt new file mode 100644 index 0000000..e69de29 diff --git a/pkg/apiserver/vshn/k8up/k8up.go b/pkg/apiserver/vshn/k8up/k8up.go new file mode 100644 index 0000000..d166b08 --- /dev/null +++ b/pkg/apiserver/vshn/k8up/k8up.go @@ -0,0 +1,99 @@ +package k8up + +import ( + "context" + "fmt" + + k8upv1 "github.com/k8up-io/k8up/v2/api/v1" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var ( + K8upGVR = schema.GroupVersionResource{ + Group: k8upv1.GroupVersion.Group, + Version: k8upv1.GroupVersion.Version, + Resource: "snapshots", + } +) + +var _ Snapshothandler = &ConcreteSnapshotHandler{} + +// Snapshothandler is an interface that handles listing and getting of k8up snapsthots. +type Snapshothandler interface { + Get(ctx context.Context, id, instanceNamespace string) (*k8upv1.Snapshot, error) + List(ctx context.Context, instanceNamespace string) (*k8upv1.SnapshotList, error) + Watch(ctx context.Context, namespace string, options *metainternalversion.ListOptions) (watch.Interface, error) + GetFromEvent(in watch.Event) (*k8upv1.Snapshot, error) +} + +// ConcreteSnapshotHandler implements Snapshothandler. +type ConcreteSnapshotHandler struct { + client client.WithWatch +} + +// Get returns the snapshot with the given ID. +func (c *ConcreteSnapshotHandler) Get(ctx context.Context, id, instanceNamespace string) (*k8upv1.Snapshot, error) { + + snapshot := &k8upv1.Snapshot{} + + err := c.client.Get(ctx, client.ObjectKey{Namespace: instanceNamespace, Name: id}, snapshot) + if err != nil { + return snapshot, err + } + + return snapshot, nil +} + +// List returns all snapshots in the given namespace. +func (c *ConcreteSnapshotHandler) List(ctx context.Context, instanceNamespace string) (*k8upv1.SnapshotList, error) { + + snapshots := &k8upv1.SnapshotList{} + + err := c.client.List(ctx, snapshots, &client.ListOptions{Namespace: instanceNamespace}) + if err != nil { + return nil, err + } + + return snapshots, nil +} + +// Watch returns a watcher for the given objects +func (c *ConcreteSnapshotHandler) Watch(ctx context.Context, namespace string, options *metainternalversion.ListOptions) (watch.Interface, error) { + + snapshots, err := c.List(ctx, namespace) + if err != nil { + return nil, err + } + + return c.client.Watch(ctx, snapshots) + +} + +// GetFromEvent resolves watch.Event into k8upv1.Snapshot +func (c *ConcreteSnapshotHandler) GetFromEvent(in watch.Event) (*k8upv1.Snapshot, error) { + snap, ok := in.Object.(*unstructured.Unstructured) + if !ok { + return nil, fmt.Errorf("cannot parse snapshot from watch event") + } + + snapshot := &k8upv1.Snapshot{} + + err := runtime.DefaultUnstructuredConverter.FromUnstructured(snap.Object, snapshot) + if err != nil { + return nil, err + } + + return snapshot, nil +} + +// New returns a new Snapshothandler implemented by ConcreteSnapshotHandler. +func New(client client.WithWatch) Snapshothandler { + return &ConcreteSnapshotHandler{ + client: client, + } +} diff --git a/pkg/apiserver/vshn/postgres/backup.go b/pkg/apiserver/vshn/postgres/backup.go new file mode 100644 index 0000000..c138267 --- /dev/null +++ b/pkg/apiserver/vshn/postgres/backup.go @@ -0,0 +1,59 @@ +package postgres + +import ( + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + vshnv1 "github.com/vshn/appcat-apiserver/apis/vshn/v1" + "k8s.io/apimachinery/pkg/runtime" + genericregistry "k8s.io/apiserver/pkg/registry/generic" + "k8s.io/apiserver/pkg/registry/rest" + "k8s.io/client-go/dynamic" + restbuilder "sigs.k8s.io/apiserver-runtime/pkg/builder/rest" + "sigs.k8s.io/apiserver-runtime/pkg/util/loopback" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// New returns a new storage provider for VSHNPostgresBackup +func New() restbuilder.ResourceHandlerProvider { + return func(s *runtime.Scheme, gasdf genericregistry.RESTOptionsGetter) (rest.Storage, error) { + c, err := client.New(loopback.GetLoopbackMasterClientConfig(), client.Options{}) + if err != nil { + return nil, err + } + + err = vshnv1.AddToScheme(c.Scheme()) + if err != nil { + return nil, err + } + + dc, err := dynamic.NewForConfig(loopback.GetLoopbackMasterClientConfig()) + if err != nil { + return nil, err + } + return &vshnPostgresBackupStorage{ + sgbackups: &kubeSGBackupProvider{ + DynamicClient: dc.Resource(sgbackupGroupVersionResource), + }, + vshnpostgresql: &kubeXVSHNPostgresqlProvider{ + Client: c, + }, + }, nil + } +} + +type vshnPostgresBackupStorage struct { + sgbackups sgbackupProvider + vshnpostgresql vshnPostgresqlProvider +} + +func (v vshnPostgresBackupStorage) New() runtime.Object { + return &v1.VSHNPostgresBackup{} +} + +func (v vshnPostgresBackupStorage) Destroy() {} + +var _ rest.Scoper = &vshnPostgresBackupStorage{} +var _ rest.Storage = &vshnPostgresBackupStorage{} + +func (v *vshnPostgresBackupStorage) NamespaceScoped() bool { + return true +} diff --git a/pkg/apiserver/vshn/postgres/backup_test.go b/pkg/apiserver/vshn/postgres/backup_test.go new file mode 100644 index 0000000..68d6115 --- /dev/null +++ b/pkg/apiserver/vshn/postgres/backup_test.go @@ -0,0 +1,138 @@ +package postgres + +import ( + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + "github.com/vshn/appcat-apiserver/test/mocks" + + "testing" + + vshnv1 "github.com/vshn/appcat-apiserver/apis/vshn/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" + + "github.com/golang/mock/gomock" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// newMockedVSHNPostgresBackupStorage is a mocked instance of vshnPostgresBackup +func newMockedVSHNPostgresBackupStorage(t *testing.T, ctrl *gomock.Controller) (rest.StandardStorage, *mocks.MocksgbackupProvider, *mocks.MockvshnPostgresqlProvider) { + t.Helper() + sgbackup := mocks.NewMocksgbackupProvider(ctrl) + vshnpostgres := mocks.NewMockvshnPostgresqlProvider(ctrl) + stor := &vshnPostgresBackupStorage{ + sgbackups: sgbackup, + vshnpostgresql: vshnpostgres, + } + return rest.Storage(stor).(rest.StandardStorage), sgbackup, vshnpostgres +} + +// Test AppCat instances +var ( + vshnBackupOne = &v1.VSHNPostgresBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "one", + Namespace: "namespace", + }, + Status: v1.VSHNPostgresBackupStatus{ + DatabaseInstance: "postgres-one", + Process: &runtime.RawExtension{Object: &unstructured.Unstructured{Object: map[string]interface{}{"status": "Failed"}}}, + BackupInformation: &runtime.RawExtension{Object: &unstructured.Unstructured{Object: map[string]interface{}{"disk": "1GB", "cpu": "1"}}}, + }, + } + + backupInfoOne = &v1.SGBackupInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: "one", + Namespace: "namespace-one", + }, + Process: runtime.RawExtension{Object: &unstructured.Unstructured{Object: map[string]interface{}{"status": "Failed"}}}, + BackupInformation: runtime.RawExtension{Object: &unstructured.Unstructured{Object: map[string]interface{}{"disk": "1GB", "cpu": "1"}}}, + } + + unstructuredBackupOne = &unstructured.Unstructured{ + Object: map[string]interface{}{ + "metadata": map[string]interface{}{ + "name": "one", + "namespace": "namespace-one", + }, + "status": map[string]interface{}{ + "process": map[string]interface{}{ + "status": "Failed", + }, + "backupInformation": map[string]interface{}{ + "disk": "1GB", + "cpu": "1", + }, + }, + }, + } + + vshnBackupTwo = &v1.VSHNPostgresBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "two", + Namespace: "namespace", + }, + Status: v1.VSHNPostgresBackupStatus{ + DatabaseInstance: "postgres-two", + Process: &runtime.RawExtension{Object: &unstructured.Unstructured{Object: map[string]interface{}{"status": "Completed"}}}, + BackupInformation: &runtime.RawExtension{Object: &unstructured.Unstructured{Object: map[string]interface{}{"disk": "2GB", "cpu": "2"}}}, + }, + } + + backupInfoTwo = &v1.SGBackupInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: "two", + Namespace: "namespace-two", + }, + Process: runtime.RawExtension{Object: &unstructured.Unstructured{Object: map[string]interface{}{"status": "Completed"}}}, + BackupInformation: runtime.RawExtension{Object: &unstructured.Unstructured{Object: map[string]interface{}{"disk": "2GB", "cpu": "2"}}}, + } + + unstructuredBackupTwo = &unstructured.Unstructured{ + Object: map[string]interface{}{ + "metadata": map[string]interface{}{ + "name": "two", + "namespace": "namespace-two", + }, + "status": map[string]interface{}{ + "process": map[string]interface{}{ + "status": "Completed", + }, + "backupInformation": map[string]interface{}{ + "disk": "2GB", + "cpu": "2", + }, + }, + }, + } + + vshnPostgreSQLInstances = &vshnv1.XVSHNPostgreSQLList{ + Items: []vshnv1.XVSHNPostgreSQL{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "postgres-one-tty", + Labels: map[string]string{ + claimNameLabel: "postgres-one", + claimNamespaceLabel: "namespace-claim", + }, + }, + Status: vshnv1.VSHNPostgreSQLStatus{ + InstanceNamespace: "namespace-one", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "postgres-two-bbf", + Labels: map[string]string{ + claimNameLabel: "postgres-two", + claimNamespaceLabel: "namespace-claim", + }, + }, + Status: vshnv1.VSHNPostgreSQLStatus{ + InstanceNamespace: "namespace-two", + }, + }, + }, + } +) diff --git a/pkg/apiserver/vshn/postgres/create.go b/pkg/apiserver/vshn/postgres/create.go new file mode 100644 index 0000000..5ceeaf7 --- /dev/null +++ b/pkg/apiserver/vshn/postgres/create.go @@ -0,0 +1,15 @@ +package postgres + +import ( + "context" + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Creater = &vshnPostgresBackupStorage{} + +func (v vshnPostgresBackupStorage) Create(_ context.Context, _ runtime.Object, _ rest.ValidateObjectFunc, _ *metav1.CreateOptions) (runtime.Object, error) { + return nil, fmt.Errorf("method not implemented") +} diff --git a/pkg/apiserver/vshn/postgres/delete.go b/pkg/apiserver/vshn/postgres/delete.go new file mode 100644 index 0000000..0256adc --- /dev/null +++ b/pkg/apiserver/vshn/postgres/delete.go @@ -0,0 +1,29 @@ +package postgres + +import ( + "context" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.GracefulDeleter = &vshnPostgresBackupStorage{} +var _ rest.CollectionDeleter = &vshnPostgresBackupStorage{} + +func (v vshnPostgresBackupStorage) Delete(_ context.Context, name string, _ rest.ValidateObjectFunc, _ *metav1.DeleteOptions) (runtime.Object, bool, error) { + return &v1.VSHNPostgresBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + }, false, nil +} + +func (v *vshnPostgresBackupStorage) DeleteCollection(ctx context.Context, _ rest.ValidateObjectFunc, _ *metav1.DeleteOptions, _ *metainternalversion.ListOptions) (runtime.Object, error) { + return &v1.VSHNPostgresBackupList{ + Items: []v1.VSHNPostgresBackup{}, + }, nil +} diff --git a/pkg/apiserver/vshn/postgres/get.go b/pkg/apiserver/vshn/postgres/get.go new file mode 100644 index 0000000..396815f --- /dev/null +++ b/pkg/apiserver/vshn/postgres/get.go @@ -0,0 +1,49 @@ +package postgres + +import ( + "context" + "fmt" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + "github.com/vshn/appcat-apiserver/pkg/apiserver" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Getter = &vshnPostgresBackupStorage{} + +// Get returns a VSHNPostgresBackupStorage service based on stackgres SGBackup resource +func (v *vshnPostgresBackupStorage) Get(ctx context.Context, name string, _ *metav1.GetOptions) (runtime.Object, error) { + namespace, ok := request.NamespaceFrom(ctx) + if !ok { + return nil, fmt.Errorf("cannot get namespace from resource") + } + + instances, err := v.vshnpostgresql.ListXVSHNPostgreSQL(ctx, namespace) + if err != nil { + return nil, fmt.Errorf("cannot list VSHNPostgreSQL instances") + } + + var vshnBackup *v1.VSHNPostgresBackup + for _, value := range instances.Items { + backupInfo, err := v.sgbackups.GetSGBackup(ctx, name, value.Status.InstanceNamespace) + if err != nil { + resolvedErr := apiserver.ResolveError(sgbackupGroupVersionResource.GroupResource(), err) + if apierrors.IsNotFound(resolvedErr) { + continue + } + return nil, err + } + + vshnBackup = v1.NewVSHNPostgresBackup(backupInfo, value.Labels[claimNameLabel], namespace) + } + + if vshnBackup == nil { + return nil, apierrors.NewNotFound(v1.New().GetGroupVersionResource().GroupResource(), name) + } + + return vshnBackup, nil +} diff --git a/pkg/apiserver/vshn/postgres/get_test.go b/pkg/apiserver/vshn/postgres/get_test.go new file mode 100644 index 0000000..7e47170 --- /dev/null +++ b/pkg/apiserver/vshn/postgres/get_test.go @@ -0,0 +1,108 @@ +package postgres + +import ( + "testing" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + vshnv1 "github.com/vshn/appcat-apiserver/apis/vshn/v1" + "github.com/vshn/appcat-apiserver/test/mocks" + apierrors "k8s.io/apimachinery/pkg/api/errors" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "k8s.io/apiserver/pkg/endpoints/request" +) + +func TestVSHNPostgresBackupStorage_Get(t *testing.T) { + tests := map[string]struct { + name string + postgresqls *vshnv1.XVSHNPostgreSQLList + backupInfo *v1.SGBackupInfo + backupInfoCalls func(mocks.MocksgbackupProvider, string) + vshnPostgresBackup *v1.VSHNPostgresBackup + err error + }{ + "GivenAListOfPostgresAndBackups_ThenVSHNPostgresBackup": { + name: "one", + postgresqls: vshnPostgreSQLInstances, + backupInfo: backupInfoOne, + backupInfoCalls: func(provider mocks.MocksgbackupProvider, name string) { + provider.EXPECT(). + GetSGBackup(gomock.Any(), name, "namespace-one"). + Return(backupInfoOne, nil). + Times(1) + + provider.EXPECT(). + GetSGBackup(gomock.Any(), name, "namespace-two"). + Return(nil, apierrors.NewNotFound(v1.GetGroupResource(v1.ResourceBackup), name)). + Times(1) + }, + vshnPostgresBackup: vshnBackupOne, + err: nil, + }, + "GivenErrNotFound_ThenErrNotFound": { + name: "one", + postgresqls: vshnPostgreSQLInstances, + backupInfo: nil, + backupInfoCalls: func(provider mocks.MocksgbackupProvider, name string) { + provider.EXPECT(). + GetSGBackup(gomock.Any(), name, "namespace-one"). + Return(nil, apierrors.NewNotFound(v1.GetGroupResource(v1.ResourceBackup), name)). + Times(1) + + provider.EXPECT(). + GetSGBackup(gomock.Any(), name, "namespace-two"). + Return(nil, apierrors.NewNotFound(v1.GetGroupResource(v1.ResourceBackup), name)). + Times(1) + }, + vshnPostgresBackup: nil, + err: apierrors.NewNotFound(v1.GetGroupResource(v1.ResourceBackup), "one"), + }, + "GivenNoPostgresInstances_ThenErrNotFound": { + name: "one", + postgresqls: &vshnv1.XVSHNPostgreSQLList{}, + backupInfo: nil, + backupInfoCalls: func(provider mocks.MocksgbackupProvider, name string) { + provider.EXPECT(). + GetSGBackup(gomock.Any(), gomock.Any(), gomock.Any()). + Times(0) + }, + vshnPostgresBackup: nil, + err: apierrors.NewNotFound(v1.GetGroupResource(v1.ResourceBackup), "one"), + }, + } + + for n, tc := range tests { + + t.Run(n, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + s, backupProvider, postgresProvider := newMockedVSHNPostgresBackupStorage(t, ctrl) + + postgresProvider.EXPECT(). + ListXVSHNPostgreSQL(gomock.Any(), gomock.Any()). + Return(tc.postgresqls, nil). + Times(1) + + tc.backupInfoCalls(*backupProvider, tc.name) + + actual, err := s.Get(request.WithRequestInfo( + request.WithNamespace(request.NewContext(), "namespace"), + &request.RequestInfo{ + Verb: "get", + APIGroup: v1.GroupVersion.Group, + Resource: v1.ResourceBackup, + Name: tc.name, + }), + tc.name, nil) + if tc.err != nil { + assert.EqualError(t, err, tc.err.Error()) + return + } + require.NoError(t, err) + assert.Equal(t, tc.vshnPostgresBackup, actual) + }) + } +} diff --git a/pkg/apiserver/vshn/postgres/list.go b/pkg/apiserver/vshn/postgres/list.go new file mode 100644 index 0000000..4b9a08f --- /dev/null +++ b/pkg/apiserver/vshn/postgres/list.go @@ -0,0 +1,105 @@ +package postgres + +import ( + "context" + "fmt" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + "github.com/vshn/appcat-apiserver/pkg/apiserver" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Lister = &vshnPostgresBackupStorage{} + +func (v *vshnPostgresBackupStorage) NewList() runtime.Object { + return &v1.VSHNPostgresBackupList{} +} + +// List returns a list of VSHNPostgresBackup services based on stackgres SGBackup resources +func (v *vshnPostgresBackupStorage) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) { + namespace, ok := request.NamespaceFrom(ctx) + if !ok { + return nil, fmt.Errorf("cannot get namespace from resource") + } + + instances, err := v.vshnpostgresql.ListXVSHNPostgreSQL(ctx, namespace) + if err != nil { + return nil, fmt.Errorf("cannot list VSHNPostgreSQL instances %v", err) + } + + // Aggregate all backups from all postgres clusters from the same namespace + backups := v1.VSHNPostgresBackupList{} + for _, value := range instances.Items { + bis, err := v.sgbackups.ListSGBackup(ctx, value.Status.InstanceNamespace, options) + if err != nil { + return nil, apiserver.ResolveError(v1.GetGroupResource(v1.ResourceBackup), err) + } + for _, b := range *bis { + vb := v1.NewVSHNPostgresBackup(&b, value.Labels[claimNameLabel], namespace) + if vb != nil { + backups.Items = append(backups.Items, *vb) + } + } + } + + return &backups, nil +} + +var _ rest.Watcher = &vshnPostgresBackupStorage{} + +// Watch returns a watched list of VSHNPostgresBackup services based on stackgres SGBackup resources +func (v *vshnPostgresBackupStorage) Watch(ctx context.Context, options *metainternalversion.ListOptions) (watch.Interface, error) { + namespace, ok := request.NamespaceFrom(ctx) + if !ok { + return nil, fmt.Errorf("cannot get namespace from resource") + } + + instances, err := v.vshnpostgresql.ListXVSHNPostgreSQL(ctx, namespace) + if err != nil { + return nil, fmt.Errorf("cannot list VSHNPostgreSQL instances") + } + + mw := apiserver.NewEmptyMultiWatch() + for _, value := range instances.Items { + backupWatcher, err := v.sgbackups.WatchSGBackup(ctx, value.Status.InstanceNamespace, options) + if err != nil { + return nil, apiserver.ResolveError(v1.GetGroupResource(v1.ResourceBackup), err) + } + mw.AddWatcher(backupWatcher) + } + + return watch.Filter(mw, func(in watch.Event) (out watch.Event, keep bool) { + if in.Object == nil { + // This should never happen, let downstream deal with it + return in, true + } + + sgbackupInfo := GetFromEvent(in) + if sgbackupInfo == nil { + return in, true + } + + db := "" + for _, value := range instances.Items { + if value.Status.InstanceNamespace == sgbackupInfo.Namespace { + db = value.Labels[claimNameLabel] + } + } + + if db == "" { + return in, false + } + + in.Object = v1.NewVSHNPostgresBackup(sgbackupInfo, db, namespace) + + if in.Object.(*v1.VSHNPostgresBackup) == nil { + return in, false + } + + return in, true + }), nil +} diff --git a/pkg/apiserver/vshn/postgres/list_test.go b/pkg/apiserver/vshn/postgres/list_test.go new file mode 100644 index 0000000..9b4e17f --- /dev/null +++ b/pkg/apiserver/vshn/postgres/list_test.go @@ -0,0 +1,278 @@ +package postgres + +import ( + "fmt" + "testing" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + vshnv1 "github.com/vshn/appcat-apiserver/apis/vshn/v1" + "github.com/vshn/appcat-apiserver/test/mocks" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "k8s.io/apiserver/pkg/endpoints/request" +) + +func TestVSHNPostgresBackupStorage_List(t *testing.T) { + tests := map[string]struct { + postgresqls *vshnv1.XVSHNPostgreSQLList + postgresqlsErr error + + backupInfoCalls func(mocks.MocksgbackupProvider, error) + backupInfosErr error + + vshnBackups *v1.VSHNPostgresBackupList + err error + }{ + "GivenPostgresDataAndListOfBackupInfos_ThenReturnVshnBackups": { + postgresqls: vshnPostgreSQLInstances, + backupInfoCalls: func(provider mocks.MocksgbackupProvider, err error) { + provider.EXPECT(). + ListSGBackup(gomock.Any(), "namespace-one", gomock.Any()). + Return(&[]v1.SGBackupInfo{*backupInfoOne}, nil). + Times(1) + + provider.EXPECT(). + ListSGBackup(gomock.Any(), "namespace-two", gomock.Any()). + Return(&[]v1.SGBackupInfo{*backupInfoTwo}, err). + Times(1) + }, + vshnBackups: &v1.VSHNPostgresBackupList{ + Items: []v1.VSHNPostgresBackup{ + *vshnBackupOne, + *vshnBackupTwo, + }, + }, + }, + "GivenNoPostgresData_ThenReturnEmpty": { + postgresqls: &vshnv1.XVSHNPostgreSQLList{}, + backupInfoCalls: func(provider mocks.MocksgbackupProvider, err error) {}, + vshnBackups: &v1.VSHNPostgresBackupList{ + Items: []v1.VSHNPostgresBackup(nil), + }, + }, + "GivenNoBackups_ThenReturnEmpty": { + postgresqls: vshnPostgreSQLInstances, + backupInfoCalls: func(provider mocks.MocksgbackupProvider, err error) { + provider.EXPECT(). + ListSGBackup(gomock.Any(), "namespace-one", gomock.Any()). + Return(&[]v1.SGBackupInfo{}, nil). + Times(1) + + provider.EXPECT(). + ListSGBackup(gomock.Any(), "namespace-two", gomock.Any()). + Return(&[]v1.SGBackupInfo{}, err). + Times(1) + }, + vshnBackups: &v1.VSHNPostgresBackupList{ + Items: []v1.VSHNPostgresBackup(nil), + }, + }, + "GivenBackupErrList_ThenReturnError": { + postgresqls: &vshnv1.XVSHNPostgreSQLList{ + Items: []vshnv1.XVSHNPostgreSQL{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "postgres-two", + }, + Status: vshnv1.VSHNPostgreSQLStatus{ + InstanceNamespace: "namespace-two", + }, + }, + }, + }, + backupInfoCalls: func(provider mocks.MocksgbackupProvider, err error) { + provider.EXPECT(). + ListSGBackup(gomock.Any(), "namespace-two", gomock.Any()). + Return(&[]v1.SGBackupInfo{}, err). + Times(1) + }, + backupInfosErr: fmt.Errorf("cannot get list"), + err: fmt.Errorf("cannot get list"), + }, + } + for n, tc := range tests { + t.Run(n, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + stor, backupsProvider, vshnPostgresProvider := newMockedVSHNPostgresBackupStorage(t, ctrl) + + vshnPostgresProvider.EXPECT(). + ListXVSHNPostgreSQL(gomock.Any(), gomock.Any()). + Return(tc.postgresqls, nil). + Times(1) + + tc.backupInfoCalls(*backupsProvider, tc.backupInfosErr) + + actualList, err := stor.List(request.WithRequestInfo( + request.WithNamespace(request.NewContext(), "namespace"), + &request.RequestInfo{ + Verb: "list", + APIGroup: v1.GroupVersion.Group, + Resource: v1.ResourceBackup, + }), nil) + if tc.err != nil { + assert.EqualError(t, err, tc.err.Error()) + return + } + require.NoError(t, err) + assert.Equal(t, tc.vshnBackups, actualList) + }) + } +} + +type testWatcher struct { + events chan watch.Event + countStops int +} + +func (w *testWatcher) Stop() { + w.countStops++ +} + +func (w *testWatcher) ResultChan() <-chan watch.Event { + return w.events +} + +func TestVSHNPostgresBackupStorage_Watch(t *testing.T) { + tests := map[string]struct { + postgresqls *vshnv1.XVSHNPostgreSQLList + postgresqlsErr error + + unstructuredEvents []watch.Event + unstructuredErr error + + backupInfoCalls func(mocks.MocksgbackupProvider, error, []watch.Interface) + + stopWatchCounterOne int + stopWatchCounterTwo int + + vshnBackupEvents []watch.Event + err error + }{ + "GivenSGBackupsUnstructured_ThenVSHNBackupsEvents": { + postgresqls: vshnPostgreSQLInstances, + stopWatchCounterOne: 1, + stopWatchCounterTwo: 1, + backupInfoCalls: func(provider mocks.MocksgbackupProvider, _ error, watches []watch.Interface) { + provider.EXPECT(). + WatchSGBackup(gomock.Any(), "namespace-one", gomock.Any()). + Return(watches[0], nil). + AnyTimes() + + provider.EXPECT(). + WatchSGBackup(gomock.Any(), "namespace-two", gomock.Any()). + Return(watches[1], nil). + AnyTimes() + }, + unstructuredEvents: []watch.Event{ + { + Type: watch.Added, + Object: unstructuredBackupOne, + }, + { + Type: watch.Modified, + Object: unstructuredBackupTwo, + }, + }, + vshnBackupEvents: []watch.Event{ + { + Type: watch.Added, + Object: vshnBackupOne, + }, + { + Type: watch.Modified, + Object: vshnBackupTwo, + }, + }, + }, + "GivenErrNotFound_ThenErrNotFound": { + postgresqls: &vshnv1.XVSHNPostgreSQLList{ + Items: []vshnv1.XVSHNPostgreSQL{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "postgres-one", + }, + Status: vshnv1.VSHNPostgreSQLStatus{ + InstanceNamespace: "namespace-one", + }, + }, + }, + }, + unstructuredErr: apierrors.NewNotFound(schema.GroupResource{ + Resource: "sgbackups", + }, "not-found"), + backupInfoCalls: func(provider mocks.MocksgbackupProvider, err error, _ []watch.Interface) { + provider.EXPECT(). + WatchSGBackup(gomock.Any(), "namespace-one", gomock.Any()). + Return(nil, err). + AnyTimes() + }, + err: apierrors.NewNotFound(schema.GroupResource{ + Group: v1.GroupVersion.Group, + Resource: v1.ResourceBackup, + }, "not-found"), + }, + "GivenPostgresInstancesError_ThenError": { + postgresqlsErr: fmt.Errorf("cannot get postgres instances"), + err: fmt.Errorf("cannot list VSHNPostgreSQL instances"), + backupInfoCalls: func(provider mocks.MocksgbackupProvider, err error, _ []watch.Interface) {}, + }, + } + + for n, tc := range tests { + t.Run(n, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + stor, backupProvider, vshnPostgresProvider := newMockedVSHNPostgresBackupStorage(t, ctrl) + + backupWatcherOne := &testWatcher{ + events: make(chan watch.Event, 1), + } + backupWatcherTwo := &testWatcher{ + events: make(chan watch.Event, 1), + } + if tc.unstructuredEvents != nil { + backupWatcherOne.events <- tc.unstructuredEvents[0] + backupWatcherTwo.events <- tc.unstructuredEvents[1] + + close(backupWatcherOne.events) + close(backupWatcherTwo.events) + } + + vshnPostgresProvider.EXPECT(). + ListXVSHNPostgreSQL(gomock.Any(), gomock.Any()). + Return(tc.postgresqls, tc.postgresqlsErr). + Times(1) + + tc.backupInfoCalls(*backupProvider, tc.unstructuredErr, []watch.Interface{backupWatcherOne, backupWatcherTwo}) + + vshnBackupWatch, err := stor.Watch(request.WithRequestInfo( + request.WithNamespace(request.NewContext(), "namespace"), + &request.RequestInfo{ + Verb: "watch", + APIGroup: v1.GroupVersion.Group, + Resource: v1.ResourceBackup, + }), nil) + if tc.err != nil { + assert.EqualError(t, err, tc.err.Error()) + return + } + require.NoError(t, err) + e1 := <-vshnBackupWatch.ResultChan() + e2 := <-vshnBackupWatch.ResultChan() + vshnBackupWatch.Stop() + vshnBackupEvents := []watch.Event{e1, e2} + assert.Equal(t, tc.stopWatchCounterOne, backupWatcherOne.countStops) + assert.Equal(t, tc.stopWatchCounterTwo, backupWatcherTwo.countStops) + assert.ElementsMatch(t, tc.vshnBackupEvents, vshnBackupEvents) + }) + } + +} diff --git a/pkg/apiserver/vshn/postgres/sgbackups.go b/pkg/apiserver/vshn/postgres/sgbackups.go new file mode 100644 index 0000000..385c4b7 --- /dev/null +++ b/pkg/apiserver/vshn/postgres/sgbackups.go @@ -0,0 +1,125 @@ +package postgres + +import ( + "context" + "fmt" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" + client "k8s.io/client-go/dynamic" +) + +var ( + sgbackupGroupVersionResource = schema.GroupVersionResource{ + Group: "stackgres.io", + Version: "v1", + Resource: "sgbackups", + } +) + +// sgbackupProvider is an abstraction to interact with the K8s API +type sgbackupProvider interface { + GetSGBackup(ctx context.Context, name, namespace string) (*v1.SGBackupInfo, error) + ListSGBackup(ctx context.Context, namespace string, options *metainternalversion.ListOptions) (*[]v1.SGBackupInfo, error) + WatchSGBackup(ctx context.Context, namespace string, options *metainternalversion.ListOptions) (watch.Interface, error) +} + +type kubeSGBackupProvider struct { + DynamicClient client.NamespaceableResourceInterface +} + +// GetSGBackup fetches SGBackup resource into unstructured.Unstructured. Relevant data is saved to v1.SGBackupInfo +func (k *kubeSGBackupProvider) GetSGBackup(ctx context.Context, name, namespace string) (*v1.SGBackupInfo, error) { + unstructuredObject, err := k.DynamicClient.Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return convertToSGBackupInfo(unstructuredObject) +} + +// ListSGBackup fetches SGBackup resources into unstructured.UnstructuredList. Relevant data is saved to v1.SGBackupInfo +func (k *kubeSGBackupProvider) ListSGBackup(ctx context.Context, namespace string, options *metainternalversion.ListOptions) (*[]v1.SGBackupInfo, error) { + unstructuredList, err := k.DynamicClient.Namespace(namespace).List(ctx, metav1.ListOptions{ + Limit: options.Limit, + Continue: options.Continue, + }) + if err != nil { + return nil, err + } + + sgbackupsInfos := make([]v1.SGBackupInfo, 0, len(unstructuredList.Items)) + for _, v := range unstructuredList.Items { + backupsInfo, err := convertToSGBackupInfo(&v) + if err != nil { + continue + } + sgbackupsInfos = append(sgbackupsInfos, *backupsInfo) + } + return &sgbackupsInfos, nil +} + +// WatchSGBackup watches SGBackup resources. +func (k *kubeSGBackupProvider) WatchSGBackup(ctx context.Context, namespace string, options *metainternalversion.ListOptions) (watch.Interface, error) { + return k.DynamicClient.Namespace(namespace).Watch(ctx, metav1.ListOptions{ + TypeMeta: options.TypeMeta, + LabelSelector: options.LabelSelector.String(), + FieldSelector: options.FieldSelector.String(), + Limit: options.Limit, + Continue: options.Continue, + }) +} + +// GetFromEvent resolves watch.Event into v1.SGBackupInfo +func GetFromEvent(in watch.Event) *v1.SGBackupInfo { + u, ok := in.Object.(*unstructured.Unstructured) + if !ok { + return nil + } + + backup, err := convertToSGBackupInfo(u) + if err != nil { + return nil + } + return backup +} + +func convertToSGBackupInfo(object *unstructured.Unstructured) (*v1.SGBackupInfo, error) { + content := object.UnstructuredContent() + objectMeta, exists, err := unstructured.NestedMap(content, v1.Metadata) + if err != nil || exists == false { + return nil, fmt.Errorf("cannot parse metadata from object %s", object) + } + + name := object.GetName() + + o := &metav1.ObjectMeta{} + err = runtime.DefaultUnstructuredConverter.FromUnstructured(objectMeta, o) + if err != nil { + return nil, err + } + + p, _, err := unstructured.NestedMap(content, v1.Status, v1.Process) + if err != nil { + return nil, fmt.Errorf("cannot parse status.process field from object name %s", name) + } + + bi, _, err := unstructured.NestedMap(content, v1.Status, v1.BackupInformation) + if err != nil { + return nil, fmt.Errorf("cannot parse status.backupInformation field from object name %s", name) + } + + b := &v1.SGBackupInfo{ObjectMeta: *o} + if p != nil { + b.Process = runtime.RawExtension{Object: &unstructured.Unstructured{Object: p}} + } + if bi != nil { + b.BackupInformation = runtime.RawExtension{Object: &unstructured.Unstructured{Object: bi}} + } + + return b, nil +} diff --git a/pkg/apiserver/vshn/postgres/sgbackups_test.go b/pkg/apiserver/vshn/postgres/sgbackups_test.go new file mode 100644 index 0000000..e3d6737 --- /dev/null +++ b/pkg/apiserver/vshn/postgres/sgbackups_test.go @@ -0,0 +1,142 @@ +package postgres + +import ( + "fmt" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +func TestConvertToBackupInfo(t *testing.T) { + tests := map[string]struct { + unstr *unstructured.Unstructured + backupInfo *v1.SGBackupInfo + err error + }{ + "GivenAnUnstructuredObject_ThenBackupInfo": { + unstr: getUnstructuredObject("one"), + backupInfo: getBackupInfo("one"), + err: nil, + }, + "GivenAnUnstructuredObjectWithoutMeta_ThenError": { + unstr: getUnstructuredObjectWithoutMeta("two"), + backupInfo: nil, + err: fmt.Errorf("cannot parse metadata from object %s", getUnstructuredObjectWithoutMeta("two")), + }, + "GivenAnUnstructuredObjectWithoutProcess_ThenBackupInfo": { + unstr: getUnstructuredObjectWithoutProcess("three"), + backupInfo: getBackupInfoWithoutProcess("three"), + err: nil, + }, + "GivenAnUnstructuredObjectWithoutBI_ThenBackupInfo": { + unstr: getUnstructuredObjectWithoutBI("four"), + backupInfo: getBackupInfoWithoutBI("four"), + err: nil, + }, + } + + for n, tc := range tests { + + t.Run(n, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + actual, err := convertToSGBackupInfo(tc.unstr) + + if tc.err != nil { + assert.EqualError(t, err, err.Error()) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tc.backupInfo, actual) + }) + } +} + +func getBackupInfoWithoutProcess(name string) *v1.SGBackupInfo { + b := getBackupInfo(name) + b.Process = runtime.RawExtension{} + return b +} + +func getBackupInfoWithoutBI(name string) *v1.SGBackupInfo { + b := getBackupInfo(name) + b.BackupInformation = runtime.RawExtension{} + return b +} + +func getUnstructuredObjectWithoutMeta(name string) *unstructured.Unstructured { + unstr := getUnstructuredObject(name) + unstructured.RemoveNestedField(unstr.Object, "metadata") + return unstr +} + +func getUnstructuredObjectWithoutProcess(name string) *unstructured.Unstructured { + unstr := getUnstructuredObject(name) + unstructured.RemoveNestedField(unstr.Object, "status", "process") + return unstr +} + +func getUnstructuredObjectWithoutBI(name string) *unstructured.Unstructured { + unstr := getUnstructuredObject(name) + unstructured.RemoveNestedField(unstr.Object, "status", "backupInformation") + return unstr +} + +func getBackupInfo(name string) *v1.SGBackupInfo { + return &v1.SGBackupInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "namespaceOne", + }, + Process: runtime.RawExtension{Object: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "status": "Success", + "err": "", + }, + }}, + BackupInformation: runtime.RawExtension{Object: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "memoryUsed": "1Gb", + "cpuUsed": "1", + "finished": true, + "paths": map[string]interface{}{ + "pathOne": "/path/to/one", + "pathTwo": "/path/to/two", + }, + }, + }}, + } +} + +func getUnstructuredObject(name string) *unstructured.Unstructured { + return &unstructured.Unstructured{ + Object: map[string]interface{}{ + "metadata": map[string]interface{}{ + "name": name, + "namespace": "namespaceOne", + }, + "status": map[string]interface{}{ + "process": map[string]interface{}{ + "status": "Success", + "err": "", + }, + "nonRelevantField": true, + "backupInformation": map[string]interface{}{ + "memoryUsed": "1Gb", + "cpuUsed": "1", + "finished": true, + "paths": map[string]interface{}{ + "pathOne": "/path/to/one", + "pathTwo": "/path/to/two", + }, + }, + }, + }, + } +} diff --git a/pkg/apiserver/vshn/postgres/table.go b/pkg/apiserver/vshn/postgres/table.go new file mode 100644 index 0000000..9d359ab --- /dev/null +++ b/pkg/apiserver/vshn/postgres/table.go @@ -0,0 +1,92 @@ +package postgres + +import ( + "context" + "fmt" + "time" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/duration" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.TableConvertor = &vshnPostgresBackupStorage{} + +// ConvertToTable translates the given object to a table for kubectl printing +func (v *vshnPostgresBackupStorage) ConvertToTable(_ context.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { + var table metav1.Table + + backups := []v1.VSHNPostgresBackup{} + if meta.IsListType(obj) { + backupList, ok := obj.(*v1.VSHNPostgresBackupList) + if !ok { + return nil, fmt.Errorf("not a vshn postgres backup: %#v", obj) + } + backups = backupList.Items + } else { + backup, ok := obj.(*v1.VSHNPostgresBackup) + if !ok { + return nil, fmt.Errorf("not a vshn postgres backup: %#v", obj) + } + backups = append(backups, *backup) + } + + for _, backup := range backups { + table.Rows = append(table.Rows, backupToTableRow(&backup)) + } + + if opt, ok := tableOptions.(*metav1.TableOptions); !ok || !opt.NoHeaders { + desc := metav1.ObjectMeta{}.SwaggerDoc() + table.ColumnDefinitions = []metav1.TableColumnDefinition{ + {Name: "Backup Name", Type: "string", Format: "name", Description: desc["name"]}, + {Name: "Database Instance", Type: "string", Description: "The database instance"}, + {Name: "Finished On", Type: "string", Description: "The data is available up to this time"}, + {Name: "Status", Type: "string", Description: "The state of this backup"}, + {Name: "Age", Type: "date", Description: desc["creationTimestamp"]}, + } + } + return &table, nil +} + +func backupToTableRow(backup *v1.VSHNPostgresBackup) metav1.TableRow { + return metav1.TableRow{ + Cells: []interface{}{ + backup.GetName(), + backup.Status.DatabaseInstance, + getEndTime(backup.Status.Process), + getProcessStatus(backup.Status.Process), + duration.HumanDuration(time.Since(backup.GetCreationTimestamp().Time))}, + Object: runtime.RawExtension{Object: backup}, + } +} + +func getEndTime(process *runtime.RawExtension) string { + if process != nil && process.Object != nil { + if v, err := runtime.DefaultUnstructuredConverter.ToUnstructured(process.Object); err == nil { + if endTime, exists, _ := unstructured.NestedString(v, v1.Timing, v1.End); exists { + return endTime + } + } + } + return "" +} + +func getProcessStatus(process *runtime.RawExtension) string { + if process != nil && process.Object != nil { + unstructuredProcess, err := runtime.DefaultUnstructuredConverter.ToUnstructured(process.Object) + if err != nil { + return "" + } + + status, ok, err := unstructured.NestedString(unstructuredProcess, v1.Status) + if err != nil || !ok { + return "" + } + return status + } + return "" +} diff --git a/pkg/apiserver/vshn/postgres/table_test.go b/pkg/apiserver/vshn/postgres/table_test.go new file mode 100644 index 0000000..f0ac8f0 --- /dev/null +++ b/pkg/apiserver/vshn/postgres/table_test.go @@ -0,0 +1,65 @@ +package postgres + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func TestVSHNPostgresBackupStorage_ConvertToTable(t *testing.T) { + tests := map[string]struct { + obj runtime.Object + tableOptions runtime.Object + fail bool + nrRows int + }{ + "GivenEmptyBackup_ThenSingleRow": { + obj: &v1.VSHNPostgresBackup{}, + nrRows: 1, + }, + "GivenBackup_ThenSingleRow": { + obj: vshnBackupOne, + nrRows: 1, + }, + "GivenBackupList_ThenMultipleRow": { + obj: &v1.VSHNPostgresBackupList{ + Items: []v1.VSHNPostgresBackup{ + {}, + {}, + {}, + }, + }, + nrRows: 3, + }, + "GivenNil_ThenFail": { + obj: nil, + fail: true, + }, + "GivenNonBackup_ThenFail": { + obj: &corev1.Pod{}, + fail: true, + }, + "GivenNonBackupList_ThenFail": { + obj: &corev1.PodList{}, + fail: true, + }, + } + backupStore := &vshnPostgresBackupStorage{} + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + table, err := backupStore.ConvertToTable(context.TODO(), tc.obj, tc.tableOptions) + if tc.fail { + require.Error(t, err) + return + } + require.NoError(t, err) + assert.Len(t, table.Rows, tc.nrRows) + }) + } +} diff --git a/pkg/apiserver/vshn/postgres/update.go b/pkg/apiserver/vshn/postgres/update.go new file mode 100644 index 0000000..108f609 --- /dev/null +++ b/pkg/apiserver/vshn/postgres/update.go @@ -0,0 +1,21 @@ +package postgres + +import ( + "context" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Updater = &vshnPostgresBackupStorage{} +var _ rest.CreaterUpdater = &vshnPostgresBackupStorage{} + +func (v vshnPostgresBackupStorage) Update(_ context.Context, name string, _ rest.UpdatedObjectInfo, _ rest.ValidateObjectFunc, _ rest.ValidateObjectUpdateFunc, _ bool, _ *metav1.UpdateOptions) (runtime.Object, bool, error) { + return &v1.VSHNPostgresBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + }, false, nil +} diff --git a/pkg/apiserver/vshn/postgres/vshnpostgresql.go b/pkg/apiserver/vshn/postgres/vshnpostgresql.go new file mode 100644 index 0000000..4a09da6 --- /dev/null +++ b/pkg/apiserver/vshn/postgres/vshnpostgresql.go @@ -0,0 +1,39 @@ +package postgres + +import ( + "context" + + vshnv1 "github.com/vshn/appcat-apiserver/apis/vshn/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var ( + claimNamespaceLabel = "crossplane.io/claim-namespace" + claimNameLabel = "crossplane.io/claim-name" +) + +// vshnPostgresqlProvider is an abstraction to interact with the K8s API +type vshnPostgresqlProvider interface { + ListXVSHNPostgreSQL(ctx context.Context, namespace string) (*vshnv1.XVSHNPostgreSQLList, error) +} + +type kubeXVSHNPostgresqlProvider struct { + client.Client +} + +// ListXVSHNPostgreSQL fetches a list of XVSHNPostgreSQL. +func (k *kubeXVSHNPostgresqlProvider) ListXVSHNPostgreSQL(ctx context.Context, namespace string) (*vshnv1.XVSHNPostgreSQLList, error) { + list := &vshnv1.XVSHNPostgreSQLList{} + err := k.Client.List(ctx, list) + cleanedList := make([]vshnv1.XVSHNPostgreSQL, 0) + for _, p := range list.Items { + if p.Labels[claimNamespaceLabel] == "" || p.Labels[claimNameLabel] == "" { + continue + } + if p.Labels[claimNamespaceLabel] == namespace { + cleanedList = append(cleanedList, p) + } + } + list.Items = cleanedList + return list, err +} diff --git a/pkg/apiserver/vshn/postgres/vshnpostgresql_test.go b/pkg/apiserver/vshn/postgres/vshnpostgresql_test.go new file mode 100644 index 0000000..65f73a1 --- /dev/null +++ b/pkg/apiserver/vshn/postgres/vshnpostgresql_test.go @@ -0,0 +1,131 @@ +package postgres + +import ( + "context" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + vshnv1 "github.com/vshn/appcat-apiserver/apis/vshn/v1" + "github.com/vshn/appcat-apiserver/test/mocks" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func Test_ListXVSHNPostgreSQL(t *testing.T) { + tests := map[string]struct { + namespace string + postgresqls *vshnv1.XVSHNPostgreSQLList + expectedPostgresqls *vshnv1.XVSHNPostgreSQLList + }{ + "GivenAListOfPostgreSQLs_ThenFilter": { + namespace: "namespace-prod", + postgresqls: &vshnv1.XVSHNPostgreSQLList{ + Items: []vshnv1.XVSHNPostgreSQL{ + getInstance("prod", "namespace-prod"), + getInstance("prod-2", "namespace-prod-2"), + getInstanceWithoutLabels("prod-3"), + getInstanceWithoutLabels("prod"), + getInstanceWithoutClaimName("prod", "namespace-prod"), + getInstanceWithoutClaimName("prod-3", "namespace-prod-2"), + getInstanceWithoutClaimNamespace("prod"), + getInstanceWithoutClaimNamespace("prod-3"), + getInstance("test", "namespace-test-2"), + getInstance("test", "namespace-prod"), + }, + }, + expectedPostgresqls: &vshnv1.XVSHNPostgreSQLList{ + Items: []vshnv1.XVSHNPostgreSQL{ + getInstance("prod", "namespace-prod"), + getInstance("test", "namespace-prod"), + }, + }, + }, + "GivenAListOfPostgreSQLs_ThenFilter_2": { + namespace: "namespace-not-match", + postgresqls: &vshnv1.XVSHNPostgreSQLList{ + Items: []vshnv1.XVSHNPostgreSQL{ + getInstance("prod", "namespace-prod"), + getInstance("prod-2", "namespace-prod-2"), + getInstanceWithoutLabels("prod-3"), + getInstanceWithoutLabels("prod"), + getInstanceWithoutClaimName("prod", "namespace-prod"), + getInstanceWithoutClaimName("prod-3", "namespace-prod-2"), + getInstanceWithoutClaimNamespace("prod"), + getInstanceWithoutClaimNamespace("prod-3"), + getInstance("test", "namespace-test-2"), + getInstance("test", "namespace-prod"), + }, + }, + expectedPostgresqls: &vshnv1.XVSHNPostgreSQLList{ + Items: []vshnv1.XVSHNPostgreSQL{}, + }, + }, + } + + for n, tc := range tests { + t.Run(n, func(t *testing.T) { + + // GIVEN + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := mocks.NewMockClient(ctrl) + provider := kubeXVSHNPostgresqlProvider{ + client, + } + + client.EXPECT(). + List(gomock.Any(), gomock.Any()). + SetArg(1, *tc.postgresqls). + Times(1) + + // WHEN + instances, err := provider.ListXVSHNPostgreSQL(context.Background(), tc.namespace) + + // THEN + assert.NoError(t, err) + assert.Equal(t, tc.expectedPostgresqls, instances) + }) + } +} + +func getInstanceWithoutClaimName(name, namespace string) vshnv1.XVSHNPostgreSQL { + return vshnv1.XVSHNPostgreSQL{ + ObjectMeta: metav1.ObjectMeta{ + Name: name + "-tty", + Labels: map[string]string{ + claimNamespaceLabel: namespace, + }, + }, + } +} + +func getInstanceWithoutClaimNamespace(name string) vshnv1.XVSHNPostgreSQL { + return vshnv1.XVSHNPostgreSQL{ + ObjectMeta: metav1.ObjectMeta{ + Name: name + "-tty", + Labels: map[string]string{ + claimNameLabel: name, + }, + }, + } +} + +func getInstanceWithoutLabels(name string) vshnv1.XVSHNPostgreSQL { + return vshnv1.XVSHNPostgreSQL{ + ObjectMeta: metav1.ObjectMeta{ + Name: name + "-tty", + }, + } +} + +func getInstance(name, namespace string) vshnv1.XVSHNPostgreSQL { + return vshnv1.XVSHNPostgreSQL{ + ObjectMeta: metav1.ObjectMeta{ + Name: name + "-tty", + Labels: map[string]string{ + claimNameLabel: name, + claimNamespaceLabel: namespace, + }, + }, + } +} diff --git a/pkg/apiserver/vshn/redis/backup.go b/pkg/apiserver/vshn/redis/backup.go new file mode 100644 index 0000000..cd90bfc --- /dev/null +++ b/pkg/apiserver/vshn/redis/backup.go @@ -0,0 +1,73 @@ +package redis + +import ( + k8upv1 "github.com/k8up-io/k8up/v2/api/v1" + appcatv1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + "github.com/vshn/appcat-apiserver/pkg/apiserver/vshn/k8up" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + genericregistry "k8s.io/apiserver/pkg/registry/generic" + "k8s.io/apiserver/pkg/registry/rest" + restbuilder "sigs.k8s.io/apiserver-runtime/pkg/builder/rest" + "sigs.k8s.io/apiserver-runtime/pkg/util/loopback" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ rest.Scoper = &vshnRedisBackupStorage{} +var _ rest.Storage = &vshnRedisBackupStorage{} + +type vshnRedisBackupStorage struct { + snapshothandler k8up.Snapshothandler + vshnRedis vshnRedisProvider +} + +// New returns a new resthandler for Redis backups. +func New() restbuilder.ResourceHandlerProvider { + return func(s *runtime.Scheme, gasdf genericregistry.RESTOptionsGetter) (rest.Storage, error) { + c, err := client.NewWithWatch(loopback.GetLoopbackMasterClientConfig(), client.Options{}) + if err != nil { + return nil, err + } + + _ = k8upv1.AddToScheme(c.Scheme()) + + return &vshnRedisBackupStorage{ + snapshothandler: k8up.New(c), + vshnRedis: &concreteRedisProvider{ + client: c, + }, + }, nil + } +} + +func (v vshnRedisBackupStorage) New() runtime.Object { + return &appcatv1.VSHNRedisBackup{} +} + +func (v vshnRedisBackupStorage) Destroy() {} + +func (v *vshnRedisBackupStorage) NamespaceScoped() bool { + return true +} + +func trimStringLength(in string) string { + length := len(in) + if length > 8 { + length = 8 + } + return in[:length] +} + +func deRefString(in *string) string { + if in == nil { + return "" + } + return *in +} + +func deRefMetaTime(in *metav1.Time) metav1.Time { + if in == nil { + return metav1.Now() + } + return *in +} diff --git a/pkg/apiserver/vshn/redis/common_test.go b/pkg/apiserver/vshn/redis/common_test.go new file mode 100644 index 0000000..39ce918 --- /dev/null +++ b/pkg/apiserver/vshn/redis/common_test.go @@ -0,0 +1,64 @@ +package redis + +import ( + "context" + + k8upv1 "github.com/k8up-io/k8up/v2/api/v1" + vshnv1 "github.com/vshn/appcat-apiserver/apis/vshn/v1" + "github.com/vshn/appcat-apiserver/pkg/apiserver/vshn/k8up" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + "k8s.io/apimachinery/pkg/watch" +) + +var _ vshnRedisProvider = &mockprovider{} +var _ k8up.Snapshothandler = &mockhandler{} + +type mockprovider struct { + err error + instances *vshnv1.VSHNRedisList +} + +func (m *mockprovider) ListVSHNRedis(ctx context.Context, namespace string) (*vshnv1.VSHNRedisList, error) { + + instances := &vshnv1.VSHNRedisList{ + Items: []vshnv1.VSHNRedis{}, + } + + for _, instance := range m.instances.Items { + if instance.GetNamespace() == namespace { + instances.Items = append(instances.Items, instance) + } + } + + return instances, m.err +} + +type mockhandler struct { + snapshot *k8upv1.Snapshot + snapshots *k8upv1.SnapshotList +} + +func (m *mockhandler) Get(ctx context.Context, id, instanceNamespace string) (*k8upv1.Snapshot, error) { + return m.snapshot, nil +} + +func (m *mockhandler) List(ctx context.Context, instanceNamespace string) (*k8upv1.SnapshotList, error) { + + snapshots := &k8upv1.SnapshotList{ + Items: []k8upv1.Snapshot{}, + } + + for _, snap := range m.snapshots.Items { + if snap.GetNamespace() == instanceNamespace { + snapshots.Items = append(snapshots.Items, snap) + } + } + + return snapshots, nil +} +func (m *mockhandler) Watch(ctx context.Context, namespace string, options *metainternalversion.ListOptions) (watch.Interface, error) { + return nil, nil +} +func (m *mockhandler) GetFromEvent(in watch.Event) (*k8upv1.Snapshot, error) { + return nil, nil +} diff --git a/pkg/apiserver/vshn/redis/create.go b/pkg/apiserver/vshn/redis/create.go new file mode 100644 index 0000000..b64d8d0 --- /dev/null +++ b/pkg/apiserver/vshn/redis/create.go @@ -0,0 +1,16 @@ +package redis + +import ( + "context" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Creater = &vshnRedisBackupStorage{} + +func (v vshnRedisBackupStorage) Create(_ context.Context, _ runtime.Object, _ rest.ValidateObjectFunc, _ *metav1.CreateOptions) (runtime.Object, error) { + return nil, fmt.Errorf("method not implemented") +} diff --git a/pkg/apiserver/vshn/redis/delete.go b/pkg/apiserver/vshn/redis/delete.go new file mode 100644 index 0000000..5a6d603 --- /dev/null +++ b/pkg/apiserver/vshn/redis/delete.go @@ -0,0 +1,29 @@ +package redis + +import ( + "context" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.GracefulDeleter = &vshnRedisBackupStorage{} +var _ rest.CollectionDeleter = &vshnRedisBackupStorage{} + +func (v vshnRedisBackupStorage) Delete(_ context.Context, name string, _ rest.ValidateObjectFunc, _ *metav1.DeleteOptions) (runtime.Object, bool, error) { + return &v1.VSHNRedisBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + }, false, nil +} + +func (v *vshnRedisBackupStorage) DeleteCollection(ctx context.Context, _ rest.ValidateObjectFunc, _ *metav1.DeleteOptions, _ *metainternalversion.ListOptions) (runtime.Object, error) { + return &v1.VSHNRedisBackupList{ + Items: []v1.VSHNRedisBackup{}, + }, nil +} diff --git a/pkg/apiserver/vshn/redis/get.go b/pkg/apiserver/vshn/redis/get.go new file mode 100644 index 0000000..5751b16 --- /dev/null +++ b/pkg/apiserver/vshn/redis/get.go @@ -0,0 +1,55 @@ +package redis + +import ( + "context" + "fmt" + + appcatv1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Getter = &vshnRedisBackupStorage{} + +func (v *vshnRedisBackupStorage) Get(ctx context.Context, name string, opts *metav1.GetOptions) (runtime.Object, error) { + + namespace, ok := request.NamespaceFrom(ctx) + if !ok { + return nil, fmt.Errorf("cannot get namespace from context") + } + + instances, err := v.vshnRedis.ListVSHNRedis(ctx, namespace) + if err != nil { + return nil, err + } + + redisSnap := &appcatv1.VSHNRedisBackup{} + + for _, instance := range instances.Items { + ins := instance.Status.InstanceNamespace + snap, err := v.snapshothandler.Get(ctx, name, ins) + if err != nil { + if apierrors.IsNotFound(err) { + continue + } + return nil, err + } + + backupMeta := snap.ObjectMeta + backupMeta.Namespace = instance.GetNamespace() + + redisSnap = &appcatv1.VSHNRedisBackup{ + ObjectMeta: backupMeta, + Status: appcatv1.VSHNRedisBackupStatus{ + ID: deRefString(snap.Spec.ID), + Date: deRefMetaTime(snap.Spec.Date), + Instance: instance.GetName(), + }, + } + } + + return redisSnap, nil +} diff --git a/pkg/apiserver/vshn/redis/get_test.go b/pkg/apiserver/vshn/redis/get_test.go new file mode 100644 index 0000000..174ef8e --- /dev/null +++ b/pkg/apiserver/vshn/redis/get_test.go @@ -0,0 +1,110 @@ +package redis + +import ( + "context" + "testing" + "time" + + k8upv1 "github.com/k8up-io/k8up/v2/api/v1" + "github.com/stretchr/testify/assert" + appcatv1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + vshnv1 "github.com/vshn/appcat-apiserver/apis/vshn/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/utils/pointer" +) + +func Test_vshnRedisBackupStorage_Get(t *testing.T) { + tests := []struct { + name string + instanceName string + instances *vshnv1.VSHNRedisList + snapshot *k8upv1.Snapshot + wantDate metav1.Time + wantNs string + want runtime.Object + wantErr bool + }{ + { + name: "GivenExistingBackup_ThenExpectBackup", + instances: &vshnv1.VSHNRedisList{ + Items: []vshnv1.VSHNRedis{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "myinstance", + Namespace: "ns1", + }, + Status: vshnv1.VSHNRedisStatus{ + InstanceNamespace: "ins1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "myinstance", + Namespace: "ns2", + }, + Status: vshnv1.VSHNRedisStatus{ + InstanceNamespace: "ins2", + }, + }, + }, + }, + snapshot: &k8upv1.Snapshot{ + ObjectMeta: metav1.ObjectMeta{ + Name: "myid", + Namespace: "ns1", + }, + Spec: k8upv1.SnapshotSpec{ + ID: pointer.String("myid"), + }, + }, + wantDate: metav1.Date(2023, time.April, 3, 13, 37, 0, 0, time.UTC), + wantNs: "ns1", + want: &appcatv1.VSHNRedisBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "myid", + Namespace: "ns1", + }, + Status: appcatv1.VSHNRedisBackupStatus{ + ID: "myid", + Instance: "myinstance", + Date: metav1.Date(2023, time.April, 3, 13, 37, 0, 0, time.UTC), + }, + }, + }, + { + name: "GivenNoBackup_ThenExpectEmptyObjects", + instances: &vshnv1.VSHNRedisList{}, + snapshot: nil, + want: &appcatv1.VSHNRedisBackup{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + ctx := request.WithNamespace(context.TODO(), tt.wantNs) + + if tt.snapshot != nil { + tt.snapshot.Spec.Date = &tt.wantDate + } + + v := &vshnRedisBackupStorage{ + vshnRedis: &mockprovider{ + instances: tt.instances, + }, + snapshothandler: &mockhandler{ + snapshot: tt.snapshot, + }, + } + got, err := v.Get(ctx, tt.instanceName, &metav1.GetOptions{}) + if (err != nil) != tt.wantErr { + t.Errorf("vshnRedisBackupStorage.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + + assert.Equal(t, tt.want, got) + + }) + } +} diff --git a/pkg/apiserver/vshn/redis/list.go b/pkg/apiserver/vshn/redis/list.go new file mode 100644 index 0000000..91b7203 --- /dev/null +++ b/pkg/apiserver/vshn/redis/list.go @@ -0,0 +1,61 @@ +package redis + +import ( + "context" + "fmt" + + appcatv1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Lister = &vshnRedisBackupStorage{} + +func (v *vshnRedisBackupStorage) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) { + + namespace, ok := request.NamespaceFrom(ctx) + if !ok { + return nil, fmt.Errorf("cannot get namespace from resource") + } + + instances, err := v.vshnRedis.ListVSHNRedis(ctx, namespace) + if err != nil { + return nil, err + } + + redisSnapshots := &appcatv1.VSHNRedisBackupList{ + Items: []appcatv1.VSHNRedisBackup{}, + } + + for _, instance := range instances.Items { + snapshots, err := v.snapshothandler.List(ctx, instance.Status.InstanceNamespace) + if err != nil && !apierrors.IsNotFound(err) { + return nil, err + } + + for _, snap := range snapshots.Items { + + backupMeta := snap.ObjectMeta + backupMeta.Namespace = instance.GetNamespace() + + redisSnapshots.Items = append(redisSnapshots.Items, appcatv1.VSHNRedisBackup{ + ObjectMeta: backupMeta, + Status: appcatv1.VSHNRedisBackupStatus{ + ID: deRefString(snap.Spec.ID), + Date: deRefMetaTime(snap.Spec.Date), + Instance: instance.GetName(), + }, + }) + } + + } + + return redisSnapshots, nil +} + +func (v *vshnRedisBackupStorage) NewList() runtime.Object { + return &appcatv1.VSHNRedisBackupList{} +} diff --git a/pkg/apiserver/vshn/redis/list_test.go b/pkg/apiserver/vshn/redis/list_test.go new file mode 100644 index 0000000..6113c7c --- /dev/null +++ b/pkg/apiserver/vshn/redis/list_test.go @@ -0,0 +1,154 @@ +package redis + +import ( + "context" + "testing" + "time" + + k8upv1 "github.com/k8up-io/k8up/v2/api/v1" + "github.com/stretchr/testify/assert" + appcatv1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + vshnv1 "github.com/vshn/appcat-apiserver/apis/vshn/v1" + "k8s.io/apimachinery/pkg/apis/meta/internalversion" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/utils/pointer" +) + +func Test_vshnRedisBackupStorage_List(t *testing.T) { + tests := []struct { + name string + instances *vshnv1.VSHNRedisList + snapshots *k8upv1.SnapshotList + wantNs string + want runtime.Object + wantDate metav1.Time + wantErr bool + }{ + { + name: "GivenAvailableBackups_ThenExpectBackupList", + instances: &vshnv1.VSHNRedisList{ + Items: []vshnv1.VSHNRedis{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "myinstance", + Namespace: "ns1", + }, + Status: vshnv1.VSHNRedisStatus{ + InstanceNamespace: "ins1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "mysecondinstance", + Namespace: "ns1", + }, + Status: vshnv1.VSHNRedisStatus{ + InstanceNamespace: "ins2", + }, + }, + }, + }, + snapshots: &k8upv1.SnapshotList{ + Items: []k8upv1.Snapshot{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "snap1", + Namespace: "ins1", + }, + Spec: k8upv1.SnapshotSpec{ + ID: pointer.String("snap1"), + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "snap2", + Namespace: "ins2", + }, + Spec: k8upv1.SnapshotSpec{ + ID: pointer.String("snap2"), + }, + }, + }, + }, + wantNs: "ns1", + wantDate: metav1.Date(2023, time.April, 3, 13, 37, 0, 0, time.UTC), + want: &appcatv1.VSHNRedisBackupList{ + Items: []appcatv1.VSHNRedisBackup{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "snap1", + Namespace: "ns1", + }, + Status: appcatv1.VSHNRedisBackupStatus{ + ID: "snap1", + Instance: "myinstance", + Date: metav1.Date(2023, time.April, 3, 13, 37, 0, 0, time.UTC), + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "snap2", + Namespace: "ns1", + }, + Status: appcatv1.VSHNRedisBackupStatus{ + ID: "snap2", + Instance: "mysecondinstance", + Date: metav1.Date(2023, time.April, 3, 13, 37, 0, 0, time.UTC), + }, + }, + }, + }, + }, + { + name: "GivenNobackups_ThenExpectEmptyList", + instances: &vshnv1.VSHNRedisList{ + Items: []vshnv1.VSHNRedis{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "myinstance", + Namespace: "ns1", + }, + Status: vshnv1.VSHNRedisStatus{ + InstanceNamespace: "ins1", + }, + }, + }, + }, + snapshots: &k8upv1.SnapshotList{ + Items: []k8upv1.Snapshot{}, + }, + wantNs: "ns1", + want: &appcatv1.VSHNRedisBackupList{ + Items: []appcatv1.VSHNRedisBackup{}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + ctx := request.WithNamespace(context.TODO(), tt.wantNs) + + for i := range tt.snapshots.Items { + tt.snapshots.Items[i].Spec.Date = &tt.wantDate + } + + v := &vshnRedisBackupStorage{ + snapshothandler: &mockhandler{ + snapshots: tt.snapshots, + }, + vshnRedis: &mockprovider{ + instances: tt.instances, + }, + } + got, err := v.List(ctx, &internalversion.ListOptions{}) + if (err != nil) != tt.wantErr { + t.Errorf("vshnRedisBackupStorage.List() error = %v, wantErr %v", err, tt.wantErr) + return + } + + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/apiserver/vshn/redis/table.go b/pkg/apiserver/vshn/redis/table.go new file mode 100644 index 0000000..9d6c1e7 --- /dev/null +++ b/pkg/apiserver/vshn/redis/table.go @@ -0,0 +1,59 @@ +package redis + +import ( + "context" + "fmt" + + appcatv1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.TableConvertor = &vshnRedisBackupStorage{} + +func (v *vshnRedisBackupStorage) ConvertToTable(_ context.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { + + table := &metav1.Table{} + + backups := []appcatv1.VSHNRedisBackup{} + if meta.IsListType(obj) { + backupList, ok := obj.(*appcatv1.VSHNRedisBackupList) + if !ok { + return nil, fmt.Errorf("not a vshn redis backup: %#v", obj) + } + backups = backupList.Items + } else { + backup, ok := obj.(*appcatv1.VSHNRedisBackup) + if !ok { + return nil, fmt.Errorf("not a vshn redis backup: %#v", obj) + } + backups = append(backups, *backup) + } + + for i := range backups { + table.Rows = append(table.Rows, backupToTableRow(&backups[i])) + } + + if opt, ok := tableOptions.(*metav1.TableOptions); !ok || !opt.NoHeaders { + table.ColumnDefinitions = []metav1.TableColumnDefinition{ + {Name: "Backup ID", Type: "string", Format: "name", Description: "ID of the snapshot"}, + {Name: "Database Instance", Type: "string", Description: "The redis instance"}, + {Name: "Backup Time", Type: "string", Description: "When backup was made"}, + } + } + + return table, nil +} + +func backupToTableRow(backup *appcatv1.VSHNRedisBackup) metav1.TableRow { + + return metav1.TableRow{ + Cells: []interface{}{ + trimStringLength(backup.Status.ID), + backup.Status.Instance, + backup.Status.Date}, + Object: runtime.RawExtension{Object: backup}, + } +} diff --git a/pkg/apiserver/vshn/redis/table_test.go b/pkg/apiserver/vshn/redis/table_test.go new file mode 100644 index 0000000..58fba4e --- /dev/null +++ b/pkg/apiserver/vshn/redis/table_test.go @@ -0,0 +1,109 @@ +package redis + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + vshnv1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func Test_vshnRedisBackupStorage_ConvertToTable(t *testing.T) { + tests := []struct { + name string + obj runtime.Object + tableOptions runtime.Object + wantRows int + wantErr bool + }{ + { + name: "GivenOneBackup_ThenExpectOneRow", + obj: &vshnv1.VSHNRedisBackup{}, + wantRows: 1, + }, + { + name: "GivenMiltipleBackups_ThenExpectMultipleRows", + obj: &vshnv1.VSHNRedisBackupList{ + Items: []vshnv1.VSHNRedisBackup{ + {}, + {}, + {}, + }, + }, + wantRows: 3, + }, + { + name: "GivenNoBackupObject_ThenExpectError", + obj: &vshnv1.AppCat{}, + wantErr: true, + }, + { + name: "GivenNil_ThenExpectError", + obj: nil, + wantErr: true, + }, + { + name: "GivenNonBackupList_THenExpectError", + obj: &vshnv1.VSHNPostgresBackupList{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := &vshnRedisBackupStorage{} + got, err := v.ConvertToTable(context.TODO(), tt.obj, tt.tableOptions) + if tt.wantErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + + assert.Len(t, got.Rows, tt.wantRows) + }) + } +} + +func Test_vshnRedisBackupStorage_ConvertToTable_noduplicate(t *testing.T) { + v := &vshnRedisBackupStorage{} + obj := vshnv1.VSHNRedisBackupList{ + Items: []vshnv1.VSHNRedisBackup{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "foo1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "foo2", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "foo3", + }, + }, + }, + } + got, err := v.ConvertToTable(context.TODO(), &obj, nil) + assert.NoError(t, err) + assert.Len(t, got.Rows, 3) + + foo1, ok := got.Rows[0].Object.Object.(*vshnv1.VSHNRedisBackup) + if assert.True(t, ok, "unexpected type for foo1") { + assert.Equal(t, "foo1", foo1.Namespace) + } + foo2, ok := got.Rows[1].Object.Object.(*vshnv1.VSHNRedisBackup) + if assert.True(t, ok, "unexpected type for foo1") { + assert.Equal(t, "foo2", foo2.Namespace) + } + foo3, ok := got.Rows[2].Object.Object.(*vshnv1.VSHNRedisBackup) + if assert.True(t, ok, "unexpected type for foo1") { + assert.Equal(t, "foo3", foo3.Namespace) + } + +} diff --git a/pkg/apiserver/vshn/redis/update.go b/pkg/apiserver/vshn/redis/update.go new file mode 100644 index 0000000..baa52f1 --- /dev/null +++ b/pkg/apiserver/vshn/redis/update.go @@ -0,0 +1,18 @@ +package redis + +import ( + "context" + "fmt" + + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Updater = &vshnRedisBackupStorage{} +var _ rest.CreaterUpdater = &vshnRedisBackupStorage{} + +func (v vshnRedisBackupStorage) Update(_ context.Context, name string, _ rest.UpdatedObjectInfo, _ rest.ValidateObjectFunc, _ rest.ValidateObjectUpdateFunc, _ bool, _ *metav1.UpdateOptions) (runtime.Object, bool, error) { + return &v1.VSHNPostgresBackup{}, false, fmt.Errorf("method not implemented") +} diff --git a/pkg/apiserver/vshn/redis/vshnredis.go b/pkg/apiserver/vshn/redis/vshnredis.go new file mode 100644 index 0000000..7d73369 --- /dev/null +++ b/pkg/apiserver/vshn/redis/vshnredis.go @@ -0,0 +1,28 @@ +package redis + +import ( + "context" + + vshnv1 "github.com/vshn/appcat-apiserver/apis/vshn/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type vshnRedisProvider interface { + ListVSHNRedis(ctx context.Context, namespace string) (*vshnv1.VSHNRedisList, error) +} + +type concreteRedisProvider struct { + client client.Client +} + +func (c *concreteRedisProvider) ListVSHNRedis(ctx context.Context, namespace string) (*vshnv1.VSHNRedisList, error) { + + instances := &vshnv1.VSHNRedisList{} + + err := c.client.List(ctx, instances, &client.ListOptions{Namespace: namespace}) + if err != nil { + return nil, err + } + + return instances, nil +} diff --git a/pkg/apiserver/vshn/redis/watch.go b/pkg/apiserver/vshn/redis/watch.go new file mode 100644 index 0000000..5ce2a0f --- /dev/null +++ b/pkg/apiserver/vshn/redis/watch.go @@ -0,0 +1,76 @@ +package redis + +import ( + "context" + "fmt" + + appcatv1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + "github.com/vshn/appcat-apiserver/pkg/apiserver" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" +) + +var _ rest.Watcher = &vshnRedisBackupStorage{} + +func (v *vshnRedisBackupStorage) Watch(ctx context.Context, options *metainternalversion.ListOptions) (watch.Interface, error) { + namespace, ok := request.NamespaceFrom(ctx) + if !ok { + return nil, fmt.Errorf("cannot get namespace from resource") + } + + instances, err := v.vshnRedis.ListVSHNRedis(ctx, namespace) + if err != nil { + return nil, fmt.Errorf("cannot list VSHNPostgreSQL instances") + } + + mw := apiserver.NewEmptyMultiWatch() + for _, value := range instances.Items { + backupWatcher, err := v.snapshothandler.Watch(ctx, value.Status.InstanceNamespace, options) + if err != nil { + return nil, apiserver.ResolveError(v1.GetGroupResource(v1.ResourceBackup), err) + } + mw.AddWatcher(backupWatcher) + } + + return watch.Filter(mw, func(in watch.Event) (out watch.Event, keep bool) { + if in.Object == nil { + // This should never happen, let downstream deal with it + return in, true + } + + backupInfo, err := v.snapshothandler.GetFromEvent(in) + if err != nil { + return in, false + } + + db := "" + namespace := "" + for _, value := range instances.Items { + if value.Status.InstanceNamespace == backupInfo.GetNamespace() { + db = value.GetName() + namespace = value.GetNamespace() + } + } + + if db == "" { + return in, false + } + + backupMeta := backupInfo.ObjectMeta + backupMeta.Namespace = namespace + + in.Object = &appcatv1.VSHNRedisBackup{ + ObjectMeta: backupMeta, + Status: v1.VSHNRedisBackupStatus{ + ID: deRefString(backupInfo.Spec.ID), + Date: deRefMetaTime(backupInfo.Spec.Date), + Instance: db, + }, + } + + return in, true + }), nil +} diff --git a/test/mocks/mock_client.go b/test/mocks/mock_client.go new file mode 100644 index 0000000..eaf344a --- /dev/null +++ b/test/mocks/mock_client.go @@ -0,0 +1,259 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: sigs.k8s.io/controller-runtime/pkg/client (interfaces: Client) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + meta "k8s.io/apimachinery/pkg/api/meta" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + client "sigs.k8s.io/controller-runtime/pkg/client" +) + +// MockClient is a mock of Client interface. +type MockClient struct { + ctrl *gomock.Controller + recorder *MockClientMockRecorder +} + +// MockClientMockRecorder is the mock recorder for MockClient. +type MockClientMockRecorder struct { + mock *MockClient +} + +// NewMockClient creates a new mock instance. +func NewMockClient(ctrl *gomock.Controller) *MockClient { + mock := &MockClient{ctrl: ctrl} + mock.recorder = &MockClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClient) EXPECT() *MockClientMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockClient) Create(arg0 context.Context, arg1 client.Object, arg2 ...client.CreateOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Create", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockClientMockRecorder) Create(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockClient)(nil).Create), varargs...) +} + +// Delete mocks base method. +func (m *MockClient) Delete(arg0 context.Context, arg1 client.Object, arg2 ...client.DeleteOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Delete", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockClientMockRecorder) Delete(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockClient)(nil).Delete), varargs...) +} + +// DeleteAllOf mocks base method. +func (m *MockClient) DeleteAllOf(arg0 context.Context, arg1 client.Object, arg2 ...client.DeleteAllOfOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteAllOf", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteAllOf indicates an expected call of DeleteAllOf. +func (mr *MockClientMockRecorder) DeleteAllOf(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAllOf", reflect.TypeOf((*MockClient)(nil).DeleteAllOf), varargs...) +} + +// Get mocks base method. +func (m *MockClient) Get(arg0 context.Context, arg1 types.NamespacedName, arg2 client.Object, arg3 ...client.GetOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1, arg2} + for _, a := range arg3 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Get", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Get indicates an expected call of Get. +func (mr *MockClientMockRecorder) Get(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClient)(nil).Get), varargs...) +} + +// GroupVersionKindFor mocks base method. +func (m *MockClient) GroupVersionKindFor(arg0 runtime.Object) (schema.GroupVersionKind, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GroupVersionKindFor", arg0) + ret0, _ := ret[0].(schema.GroupVersionKind) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GroupVersionKindFor indicates an expected call of GroupVersionKindFor. +func (mr *MockClientMockRecorder) GroupVersionKindFor(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupVersionKindFor", reflect.TypeOf((*MockClient)(nil).GroupVersionKindFor), arg0) +} + +// IsObjectNamespaced mocks base method. +func (m *MockClient) IsObjectNamespaced(arg0 runtime.Object) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsObjectNamespaced", arg0) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsObjectNamespaced indicates an expected call of IsObjectNamespaced. +func (mr *MockClientMockRecorder) IsObjectNamespaced(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsObjectNamespaced", reflect.TypeOf((*MockClient)(nil).IsObjectNamespaced), arg0) +} + +// List mocks base method. +func (m *MockClient) List(arg0 context.Context, arg1 client.ObjectList, arg2 ...client.ListOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "List", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// List indicates an expected call of List. +func (mr *MockClientMockRecorder) List(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockClient)(nil).List), varargs...) +} + +// Patch mocks base method. +func (m *MockClient) Patch(arg0 context.Context, arg1 client.Object, arg2 client.Patch, arg3 ...client.PatchOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1, arg2} + for _, a := range arg3 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Patch", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Patch indicates an expected call of Patch. +func (mr *MockClientMockRecorder) Patch(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockClient)(nil).Patch), varargs...) +} + +// RESTMapper mocks base method. +func (m *MockClient) RESTMapper() meta.RESTMapper { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RESTMapper") + ret0, _ := ret[0].(meta.RESTMapper) + return ret0 +} + +// RESTMapper indicates an expected call of RESTMapper. +func (mr *MockClientMockRecorder) RESTMapper() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RESTMapper", reflect.TypeOf((*MockClient)(nil).RESTMapper)) +} + +// Scheme mocks base method. +func (m *MockClient) Scheme() *runtime.Scheme { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Scheme") + ret0, _ := ret[0].(*runtime.Scheme) + return ret0 +} + +// Scheme indicates an expected call of Scheme. +func (mr *MockClientMockRecorder) Scheme() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Scheme", reflect.TypeOf((*MockClient)(nil).Scheme)) +} + +// Status mocks base method. +func (m *MockClient) Status() client.SubResourceWriter { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Status") + ret0, _ := ret[0].(client.SubResourceWriter) + return ret0 +} + +// Status indicates an expected call of Status. +func (mr *MockClientMockRecorder) Status() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Status", reflect.TypeOf((*MockClient)(nil).Status)) +} + +// SubResource mocks base method. +func (m *MockClient) SubResource(arg0 string) client.SubResourceClient { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SubResource", arg0) + ret0, _ := ret[0].(client.SubResourceClient) + return ret0 +} + +// SubResource indicates an expected call of SubResource. +func (mr *MockClientMockRecorder) SubResource(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubResource", reflect.TypeOf((*MockClient)(nil).SubResource), arg0) +} + +// Update mocks base method. +func (m *MockClient) Update(arg0 context.Context, arg1 client.Object, arg2 ...client.UpdateOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Update", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockClientMockRecorder) Update(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockClient)(nil).Update), varargs...) +} diff --git a/test/mocks/mock_composition.go b/test/mocks/mock_composition.go new file mode 100644 index 0000000..5280ff4 --- /dev/null +++ b/test/mocks/mock_composition.go @@ -0,0 +1,84 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../pkg/apiserver/appcat/composition.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + v1 "github.com/crossplane/crossplane/apis/apiextensions/v1" + gomock "github.com/golang/mock/gomock" + internalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + v10 "k8s.io/apimachinery/pkg/apis/meta/v1" + watch "k8s.io/apimachinery/pkg/watch" +) + +// MockcompositionProvider is a mock of compositionProvider interface. +type MockcompositionProvider struct { + ctrl *gomock.Controller + recorder *MockcompositionProviderMockRecorder +} + +// MockcompositionProviderMockRecorder is the mock recorder for MockcompositionProvider. +type MockcompositionProviderMockRecorder struct { + mock *MockcompositionProvider +} + +// NewMockcompositionProvider creates a new mock instance. +func NewMockcompositionProvider(ctrl *gomock.Controller) *MockcompositionProvider { + mock := &MockcompositionProvider{ctrl: ctrl} + mock.recorder = &MockcompositionProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockcompositionProvider) EXPECT() *MockcompositionProviderMockRecorder { + return m.recorder +} + +// GetComposition mocks base method. +func (m *MockcompositionProvider) GetComposition(ctx context.Context, name string, options *v10.GetOptions) (*v1.Composition, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetComposition", ctx, name, options) + ret0, _ := ret[0].(*v1.Composition) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetComposition indicates an expected call of GetComposition. +func (mr *MockcompositionProviderMockRecorder) GetComposition(ctx, name, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetComposition", reflect.TypeOf((*MockcompositionProvider)(nil).GetComposition), ctx, name, options) +} + +// ListCompositions mocks base method. +func (m *MockcompositionProvider) ListCompositions(ctx context.Context, options *internalversion.ListOptions) (*v1.CompositionList, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListCompositions", ctx, options) + ret0, _ := ret[0].(*v1.CompositionList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListCompositions indicates an expected call of ListCompositions. +func (mr *MockcompositionProviderMockRecorder) ListCompositions(ctx, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCompositions", reflect.TypeOf((*MockcompositionProvider)(nil).ListCompositions), ctx, options) +} + +// WatchCompositions mocks base method. +func (m *MockcompositionProvider) WatchCompositions(ctx context.Context, options *internalversion.ListOptions) (watch.Interface, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WatchCompositions", ctx, options) + ret0, _ := ret[0].(watch.Interface) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WatchCompositions indicates an expected call of WatchCompositions. +func (mr *MockcompositionProviderMockRecorder) WatchCompositions(ctx, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WatchCompositions", reflect.TypeOf((*MockcompositionProvider)(nil).WatchCompositions), ctx, options) +} diff --git a/test/mocks/mock_sgbackups.go b/test/mocks/mock_sgbackups.go new file mode 100644 index 0000000..3c9e103 --- /dev/null +++ b/test/mocks/mock_sgbackups.go @@ -0,0 +1,83 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../pkg/apiserver/vshn/postgres/sgbackups.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + v1 "github.com/vshn/appcat-apiserver/apis/appcat/v1" + internalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + watch "k8s.io/apimachinery/pkg/watch" +) + +// MocksgbackupProvider is a mock of sgbackupProvider interface. +type MocksgbackupProvider struct { + ctrl *gomock.Controller + recorder *MocksgbackupProviderMockRecorder +} + +// MocksgbackupProviderMockRecorder is the mock recorder for MocksgbackupProvider. +type MocksgbackupProviderMockRecorder struct { + mock *MocksgbackupProvider +} + +// NewMocksgbackupProvider creates a new mock instance. +func NewMocksgbackupProvider(ctrl *gomock.Controller) *MocksgbackupProvider { + mock := &MocksgbackupProvider{ctrl: ctrl} + mock.recorder = &MocksgbackupProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MocksgbackupProvider) EXPECT() *MocksgbackupProviderMockRecorder { + return m.recorder +} + +// GetSGBackup mocks base method. +func (m *MocksgbackupProvider) GetSGBackup(ctx context.Context, name, namespace string) (*v1.SGBackupInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSGBackup", ctx, name, namespace) + ret0, _ := ret[0].(*v1.SGBackupInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSGBackup indicates an expected call of GetSGBackup. +func (mr *MocksgbackupProviderMockRecorder) GetSGBackup(ctx, name, namespace interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSGBackup", reflect.TypeOf((*MocksgbackupProvider)(nil).GetSGBackup), ctx, name, namespace) +} + +// ListSGBackup mocks base method. +func (m *MocksgbackupProvider) ListSGBackup(ctx context.Context, namespace string, options *internalversion.ListOptions) (*[]v1.SGBackupInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListSGBackup", ctx, namespace, options) + ret0, _ := ret[0].(*[]v1.SGBackupInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListSGBackup indicates an expected call of ListSGBackup. +func (mr *MocksgbackupProviderMockRecorder) ListSGBackup(ctx, namespace, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSGBackup", reflect.TypeOf((*MocksgbackupProvider)(nil).ListSGBackup), ctx, namespace, options) +} + +// WatchSGBackup mocks base method. +func (m *MocksgbackupProvider) WatchSGBackup(ctx context.Context, namespace string, options *internalversion.ListOptions) (watch.Interface, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WatchSGBackup", ctx, namespace, options) + ret0, _ := ret[0].(watch.Interface) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WatchSGBackup indicates an expected call of WatchSGBackup. +func (mr *MocksgbackupProviderMockRecorder) WatchSGBackup(ctx, namespace, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WatchSGBackup", reflect.TypeOf((*MocksgbackupProvider)(nil).WatchSGBackup), ctx, namespace, options) +} diff --git a/test/mocks/mock_vshnpostgresqls.go b/test/mocks/mock_vshnpostgresqls.go new file mode 100644 index 0000000..416a62d --- /dev/null +++ b/test/mocks/mock_vshnpostgresqls.go @@ -0,0 +1,51 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../pkg/apiserver/vshn/postgres/vshnpostgresql.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + vshnv1 "github.com/vshn/appcat-apiserver/apis/vshn/v1" +) + +// MockvshnPostgresqlProvider is a mock of vshnPostgresqlProvider interface. +type MockvshnPostgresqlProvider struct { + ctrl *gomock.Controller + recorder *MockvshnPostgresqlProviderMockRecorder +} + +// MockvshnPostgresqlProviderMockRecorder is the mock recorder for MockvshnPostgresqlProvider. +type MockvshnPostgresqlProviderMockRecorder struct { + mock *MockvshnPostgresqlProvider +} + +// NewMockvshnPostgresqlProvider creates a new mock instance. +func NewMockvshnPostgresqlProvider(ctrl *gomock.Controller) *MockvshnPostgresqlProvider { + mock := &MockvshnPostgresqlProvider{ctrl: ctrl} + mock.recorder = &MockvshnPostgresqlProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockvshnPostgresqlProvider) EXPECT() *MockvshnPostgresqlProviderMockRecorder { + return m.recorder +} + +// ListXVSHNPostgreSQL mocks base method. +func (m *MockvshnPostgresqlProvider) ListXVSHNPostgreSQL(ctx context.Context, namespace string) (*vshnv1.XVSHNPostgreSQLList, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListXVSHNPostgreSQL", ctx, namespace) + ret0, _ := ret[0].(*vshnv1.XVSHNPostgreSQLList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListXVSHNPostgreSQL indicates an expected call of ListXVSHNPostgreSQL. +func (mr *MockvshnPostgresqlProviderMockRecorder) ListXVSHNPostgreSQL(ctx, namespace interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListXVSHNPostgreSQL", reflect.TypeOf((*MockvshnPostgresqlProvider)(nil).ListXVSHNPostgreSQL), ctx, namespace) +} diff --git a/tools.go b/tools.go new file mode 100644 index 0000000..45cb969 --- /dev/null +++ b/tools.go @@ -0,0 +1,24 @@ +//go:build tools +// +build tools + +// Package tools is a place to put any tooling dependencies as imports. +// Go modules will be forced to download and install them. +package tools + +import ( + // This is basically KubeBuilder + _ "sigs.k8s.io/controller-tools/cmd/controller-gen" + // To generate mocks + _ "github.com/golang/mock/mockgen" + // To have protoc generator + _ "github.com/golang/protobuf/protoc-gen-go" + // To have Kind updated via Renovate + _ "sigs.k8s.io/kind" + // To have protobuf generator + _ "k8s.io/code-generator" + // mock tool + _ "github.com/vektra/mockery/v2" + // Add any build-time dependencies here with blank imports like `_ "package"` + _ "github.com/deepmap/oapi-codegen/cmd/oapi-codegen" + _ "sigs.k8s.io/controller-tools/cmd/controller-gen" +)